Skip to main content

Full text of "Wprowadzenie do programowania w języku AWK"

See other formats


Opis JQzyka AWK 

Tomasz Przechlewski 
18 listopada 2000 roku 



Poszerzona i poprawiona wersja tekstu: B. Lichonski i T. Przechlewski, AWK 
opis jezyka z przykladami [ ]. 



Copyright (C) 2000, T. Przechlewski 

Zezwala si§ na rozpowszechnianie i modyfikowanie tego dokumentu pod warunkiem 
umieszczenia na kazdej kopii noty copyrightowej oraz niniejszej noty licencyjnej. 
Zmodyfikowana wersja dokumentu musi bye rozpowszechniana na warunkach 
niniejszej licencji. 



Spis tresci 3 

Spis tresci 

1. Wprowadzenie 5 

1.1. Struktura pliku wejsciowego 7 

1.2. Skrypty wykonywalne 9 

2. Wzorce 9 

2.1. BEGIN/END 10 

2.2. Wyrazenie 10 

2.3. Wzorzec regulamy 11 

2.4. Wzorzec zlozony 11 

2.5. Wzorzec z przecinkiem 12 

3. Wyrazenia regularne 12 

3.1. Napisy jako wyrazenia regularne 14 

4. Wyrazenia 15 

4.1. Stale 15 

4.2. Zmienne 15 

4.3. Zmienne wbudowane 16 

4.4. Zmienne przechowujace zawartoaci pol 17 

4.5. Operatory arytmetyczne 17 

4.6. Operatory napisowe 17 

4.7. Operatory porownywania i operatory logiczne 18 

4.8. Operatory pasowania do wyrazeii regularnych 19 

4.9. Przypisanie 19 

4.10. Operator warunkowy ?: 19 

5. Arytmetyczne funkcje wbudowane 20 

6. Napisowe funkcje wbudowane 20 

7. Funkcje daty i czasu 24 

8. Instrukcje sterujace 24 

8.1. Instrukcja pusta 27 

9. Tablice asocjacyjne 27 

9.1. Instrukcja delete 28 

9.2. Tablice wielowymiarowe 28 

10. Funkcje 29 



Spis tresci 



ll.Wejscie 30 

11.1. Pola 31 

11.2. Rekordy 31 

11.3. Instrukcja getline 34 

11.4. Pola o ustalonej dlugosci 36 

12.1nstrukcje wyjscia — print/printf 36 

12.1. Instrukcja printf 36 

12.2. Instrukcja print 38 

12.3. Drukowanie do plikow 38 

12.4. Drukowanie w potoku 40 

12.5. Funkcja close 40 

12.6. Funkcja f flush* 42 

12.7. Funkcja system 42 

13.Argumenty wywolania programu 42 

14.Uruchamianie AWK 43 

Bibliografia 44 

Skorowidz 44 



1. Wprowadzenie 



1. Wprowadzenie 

System UNIX wyposazony jest w wiele narzedzi wspomagajacych prace; uzytkow- 
nikow. AWK jest jednym ze standardowych narzedzi tego systemu, choc imple- 
mentacje AWK znalezc mozna niemal na kazdej platformie systemowej. Nazwa 
AWK pochodzi od inicjalow jego tworcow: Alfreda V. Aho, Petera J. Weinbergera 
i Briana W. Kernighana. 

W jednym zdaniu mozna powiedziec, ze AWK sluzy do transformacji danych 
tekstowych. Istota^ dzialania AWK jest przetwarzanie pliku lub plikow wejsciowych 
wedlug zadanego zbioru regul, generujac strumieh danych wyjsciowych, czy tez 
plikow wyjsciowych. 

Kazdy program jezyka AWK sklada si§ z dowolnej liczby par 1 : 

(wzorzec) { (akcja) } 

( Wzorzec) jest wyrazeniem logicznym, ktore moze bye prawdziwe (wowczas 
wykonywana jest (akcja)) lub falszywe ((akcja) nie jest wykonywana). Akcja jest 
zawsze zawarta pomiedzy parq nawiasow { i }. 

AWK moze bye wywolany na wiele sposobow. Jezeli program jest krotki, to 
najprosciej jest umiescic go pomi^dzy znakami pojedynczeyo cudzyslowa w linii po- 
leceh, w nastepujacy sposob (w systemach DOS/MS Windows zamiast cudzyslowa 
pojedynczego nalezy uzyc cudzyslowa maszynowego "): 

awk '(program)' (plikl) (plikz) (...) 

Kiedy program jest dlugi wygodniejsze jest jego umieszczenie w oddzielnym 
pliku; w tym wypadku uruchomienie programu wyglada nastejmjaco ((program) 
oznacza nazwe^ pliku zawierajacego program): 

awk -f (program) (plikl) (plikz) (...) 

Program w jezyku AWK moze zawierac wiele par (wzorzec) { (akcja) }. AWK 
czyta po kolei wiersze z (plikul), (plikuz) itd. dla wszystkich plikow, ktorych 
nazwy podano w linii poleceh. Pliki te sa^ modyfikowane wedlug programu z pliku 
(program), tj. dla kazdego wiersza z kazdego z plikow wejsciowych obliczane s^ 
kolejne wzorce (w kolejnosci ich wystejpowania w programie) i wykonywane akeje. 
Przyklad 1 (s. 6) pokazuje program wykorzystujacy 2 wzorce. Uwaga: Przykladowe 
programy mogq, zawierac konstrukeje w danym momencie jeszcze nie omowione. 
Jezeli cos jest niezrozumiale, czytaj dalej, a po lekturze calego tekstu wroc do 
tego miejsca - wszystko powinno bye jasne. Uwaga 2: Przedstawione przyklady 
programow sa^ gotowe do uruchomienia w systemach Unix/Linux natomiast 
w systemach DOS/MS Windows wymagajfj czasami modyfikacji, np. zamiany 
znakow ' na " czy zastapienia skryptow shellowych odpowiednimi plikami .bat. 

Przed przedstawieniem bardziej szczegolowych informacji o jezyku wymienimy 
kilka podstawowych regul skladni AWK: 

1 W przykiadach programow fraginenty, ktore oznaczaja^ pewne pojecia, a nie konkretne 
konstrukeje jezyka oznaczono kursywa^ wewnatrz nawiasow trojkatnych, np.: (instrukeja) oznacza 
kazda^ instrukeja AWK. 



1. Wprowadzenie 



• kolejne pary „[wzorzec) { [akcja) }" muszq bye oddzielone srednikami lub 
znakami nowego wiersza; 

• akcje moga^ skladac si§ z wielu poleceii, ktore muszq bye oddzielone sredni- 
kami lub znakami nowego wiersza; 

• wzorzec lub akcja moze zostac pominiety. W wypadku braku wzorca akcja 
zostaje wykonana dla kazdeyo wiersza pliku wejsciowego. Jezeli pominiemy 
akcj§, to AWK zastosuje akcje^ domysln^, jaka^ jest wydrukowanie wiersza 
z pliku wejsciowego (czyli { print $0 } w skladni AWK). 

PRZYKLAD 1 

Ponizszy program przepisuje plik wejsciowy zastepiujac kolejne puste wiersze, 
jednym pustym wierszem (autor: Nelson H. F. Beebe). 

NF == { nb++ } 

NF > {if (nb > 0) print ""; nb = 0; print $0; > 

□ 

UwAGl: Poniewaz zarowno [wzorzec) jak i [akcja) sq. opcjonalne, to stosowanie 
nast^pujacego stylu programowania: 

[wzorzec) 
{ 

[akcja) 
} 

jest bledem, gdyz powyzszy zapis jest interpretowany jako dwie pary wzorzec-akeja. 
Pierwszy wiersz jest interpretowany jako [wzorzec) nie posiadajacy jawnie wyspe- 
cyfikowanej [akcji), co oznacza wydrukowanie wszystkich wierszy z pliku wejscio- 
wego, dla ktorych wartosciq logicznq, [wzorca) jest prawda. Trzy nastejme wiersze 
sq natomiast interpretowane jako [akcja) bez jawnie podanego wzorca, co powodu- 
je, ze b^dzie ona wykonywana dla kazdeyo kolejnego wiersza z pliku wejsciowego. 
Jezeli ktos lubi tego typu styl pisania programow, to musi umiescic otwierajacy 
nawias { w jednym wierszu z odpowiadajacym mu [wzorcem) . 

Jest wiele interpretatorow AWK. W systemach uniksopodobnych sq dostarcza- 
ne razem z systemem. Istniejq tez wersje ogolnodostepne, takie jak: gawk - fir- 
mowany przez Free Software Foundation czy mawk Michaela Brennana. Rozne 
implement acje AWK nie sq w 100% kompatybilne ze sobq a ponadto wiele z nich 
posiada rozszerzenia w stosunku do standardu (za standard przyjmujemy opis 
z [1]). W niniejszym tekscie przedstawiono standard AWK i rozszerzenia interpre- 
tatora gawk w wersji 3.*. 

Doswiadczenia autora wskazujq, ze komercyjne implementacje AWK sq yorsze 
niz gawk. Przykladowo maksymalna liczba pol w rekordzie albo maksymalna dlu- 
gosc rekordu w znakach moze bye smiesznie mala (np. 99 pol w rekordzie). Z tego 
wzgl^du zachecam do zainstalowania i poslugiwania si§ gawk-iem takze w syste- 
mach, w ktorych znajduje sie^ inna implementacja AWK. Zainstalowanie gawk-a ze 



1. Wprowadzenie 



zrodel jest w wiekszosci systemow uniksowych bardzo proste (naprawdej). Oczy- 
wiscie w sklad systemu GNU/Linux standardowo wchodzi gawk zatem problem 
z glowy. Uzytkownicy systemow DOS czy Microsoft Windows, ktorych producent 
oczywiscie nie dolacza AWK, muszq samodzielnie skopiowac „z sieci" i zaistalowac 
gawk-a (lub mawk-a). Kopiujemy gotowe pliki wykonywalne poniewaz ich samo- 
dzielne kompilowanie nie jest prost^, rzeczq w systemie DOS/MS Windows. 

UwAGl: Funkcje, zmienne wbudowane oraz inne konstrukcje poszerzajace mozli- 
wosci standardowego AWK zostaly w tekscie wyroznione za pomocq, znaku * . 

1.1. Struktura pliku wejsciowego 

Dla AWK dane wejsciowe skladaja^ sie^ z rekordow, ktore rozdzielone sa^ separatora- 
mi RS. Standardowo rekordem jest caly wiersz, czyli separatorem jest znak korica 
wiersza. 

Rekordy podzielone sq na pola, ktore rozdzielone sq, separatorami pol FS. 
Domyslnie separatorami pol sq odstgpy, tj. znaki spacji i/lub tabulacji. Ponizej 
zamieszczono zawartosc pliku wina.txt, ktorego kazdy wiersz zawiera: nazw^ 
wina, symbol kraju-producenta, kolor, smak, cen^ w zlotych oraz liczbe^ butelek 
sprzedanych, na przyklad w ostatnim miesiacu: 

Chardonnay Lyngrove RPA biale wytrawne 95.00 131 

Cabernet Sauvignon Merlot Lyngrove RPA czerwone wytrawne 95 . 00 58 

Baron De France Fra biale wytrawne 25.00 289 

Anjou Blanc Chenin Fra biale wytrawne 33.00 392 

Anjou D'rose Fra rozowe polwytrawne 33.00 207 

Anjou Rouge Cabernet Fra czerwone polwytrawne 33.00 266 

Bordeaux Graveschateau Saint Galier Fra biale wytrawne 89.00 144 

Marquis De Chasse 1996 Fra czerwone wytrawne 55.00 229 

Don Kichot Tinto Spa czerwone polwytrawne 25.00 360 

Rioja Miralcampo Spa czerwone wytrawne 62.00 210 

Rioja Miralcampo Spa biale wytrawne 62.00 179 

Sherry Rich Cream Spa czerwone slodkie 109.00 55 

Sole D'italia Ita czerwone wytrawne 25.00 666 

Valpolicella Ita czerwone wytrawne 45.00 370 

Chianti Villa Bellafonte Ita czerwone wytrawne 68.00 131 

Asti Spumante Docg Ita biale polslodkie 51.00 207 

Moscato Spumante Vsq Ita biale slodkie 26.00 629 

Poniewaz nazwa wina sklada si§ z wielu wyrazow, liczba pol w poszczegolnych 
rekordach jest zmieima, np. pierwszy wiersz zawiera 7 pol, drugi 9 pol, trzeci 
8 pol, itd. 

RS i FS sa^ zmiennymi, a wi^c mozna im nadac wartosc. Przykladowo, jesli 
zmiemiej FS nadamy wartosc '; ', to separatorami pol bedq znaki '; ' a nie spacje 
i tabulatory. Wartosciq zmiemiej FS moze bye dowolne wyrazenie regularne. 



1. Wprowadzenie 



W akcjach i wzorcach do wartosci pol mozna siig odwolywac za pomoc^ 
zmiennych postaci $(nr-pola) . Tak wiigc $1 to pierwsze pole rekordu, $2 drugie 
itd. $0 oznacza caly rekord. Wbudowana zmienna NF przechowywuje liczbg pol 
biezacego rekordu, stad $NF to zmienna zawierajaca zawartosc ostatniego pola 
(w kazdym rekordzie). 

Przykladowo w pliku wina . txt zmienna $NF zawiera wielkosc sprzedazy 
kazdego gatunku wina. Dla pierwszego z win $1 zawiera napis "Chardonnay" 
a $2 napis "Lyngrove". Dla drugiego wiersza wartosciq $1 bedzie "Cabernet" 
itd. Poniewaz liczba wyrazow w nazwie wina jest nieustalona, wydawac by si§ 
mogio, ze dotarcie do odpowiednich informacji w kazdym wierszu moze bye 
skomplikowane, ale tak nie jest, bo pola mozna takze liczyc od koiica. Zapis 
$ (NF-1) oznacza pole przedostatnie, $ (NF-2) drugie od koiica itd. Nawiasy okrqgie 
s^ tutaj obowiazkowe, a ich brak zmienia znaczenie takiej konstrukeji, wi^cej 
szczegolow jest w punkcie 4.4. 

Plik wina. txt byl jednorodny w tym sensie, ze kazdy rekord (wiersz) zawieral 
informacji o jednym gatunku wina. Czesto mozna miec do czynienia z plikami, 
ktore zawieraj^ rozne rekordy, np. ponizej przedstawiono plik tdf2000.txt 
zawierajqcy zestawienie wszystkich podjazdow o nachyleniu wi^kszym od 5% na 
alpejskich etapach wyscigu Tour de France '2000: 

**** Tour de France *** 1/07/2000 — 23/07/2000 *** 3630km ***** 
Nazwa gory/przel§czy Dystans Poczatek Szczyt Roznica Dlugosc 



Etap 14: Draguignan — Briancon 










Col d' Alios 127.5 


1432 


2250 


818 


13.4 


Col de Vars 177.5 


1401 


2109 


708 


10.4 


Col d'Izoard 249.5 


1345 


2361 


1016 


14.1 


Etap 15: Briancon — Courchevel 










Col du Galibier 33.0 


2065 


2645 


580 


8.4 


Col de la Madeleine 110.5 


456 


2000 


1544 


19.3 


Monte de Courchevel 173.5 


602 


2004 


1402 


17.3 


Etap 16: Courchevel — Morzine 










Col des Saisies 80.0 


668 


1650 


982 


15.1 


Col des Aravis 106.5 


973 


1498 


525 


8.2 


Col de la Colombiere 131.0 


922 


1618 


696 


11.6 


Col de Chatillon 158.0 


478 


733 


255 


4.9 


Col de Joux-Plane 196.5 


692 


1700 


1008 


12.0 



W tym przypadku w pliku znajdujq sie^ nast^pujace grupy rekordow: naglowek 
(wiersze 1-3), okreslajace etap, zawierajace dane o kazdej gorze (nazwa, dystans 
od startu w kilometrach, wysokosc w metrach n.p.m. podnoza i szczytu, roznica 
poziomow oraz dlugosc podjazdu w kilometrach) i puste wiersze separujace. 



2. Wzorce 



Do plikow wina.txt oraz tdf2000.txt bedziemy czesto wracac w przykladach 
zamieszczonych w dalszej czesci tekstu. 



1.2. Skrypty wykonywalne 

Za pomoca^ mechanizmu znakow # ! umieszczonych jako dwa pierwsze znaki w pliku 
mozliwe jest uruchamianie programow AWK-owych, tak jakby byly programami 
wykonywalnymi (w systemach uniksowych, me w DOS/MS Windows!). Jezeli 
przykladowo umiescimy w pliku prlO nastejpujacy kod: 

# ! /usr/bin/awk -f 
NR <= 10 

to, po nadaniu plikowi prawa do wykonywania (za pomocq chmod), mozemy 
uruchamiac program piszac po prostu: prlO {plik) . (Program drukuje pierwsze 
10 wierszy [pliku).) 



2. Wzorce 

Wzorce sluza^ do wyznaczenia tych wierszy tekstu, dla ktorych wykonane majq bye 
odpowiednie akeje. W ogolnym wypadku wzorzec moze bye kombinacjq wyrazeii 
logicznych i wyrazeii regularnych. Poniewaz wzorce sq wyrazeniami logicznymi, 
dozwolone sej, operatory logiczne: &&, II, ! oraz nawiasy. Istnieja^ dwa specjalne 
wzorce o nazwie BEGIN i END. Oto ogolna specyfikacja wzorcow: 

Wzorce 

BEGlH(akcja)} 

{akcja) jest wykonywana przed otwarciem pliku wejsciowego. 

END{ (akcja)} 

{akcja) jest wykonywana po zamknieciu pliku (plikow) wejsciowego. 

{wyrazenie) {.{akcja) } 

{akcja) jest wykonywana za kazdym razem gdy wartosc {wyrazenia) jest rowna 

prawda, tj. jest niezerowa (dla wyrazeii numerycznych) lub niepusta (dla napisow). 

/ {wyrazenie-reyularne) /{{akcja)} 

{akcja) jest wykonywana za kazdym razem gdy wiersz z pliku wejsciowego zawiera 

ci^g znakow pasujacy do {wyrazenia-reyularneyo) . 

{wzorzec- zlozony) {{akcja) } 

wzorzec zlozony to kombinacja logiczna dowolnych wamnkow. Mozna stosowac 

operatory && (koniunkeja), I I (alternatywa), ! (negacja) oraz nawiasy. Por. 2.4. 



10 2. Wzorce 



(wzorzecl), (wzorzecZ) {.(akcja)"} 

(akcja) jest wykonywana dla wszystkich wierszy od wiersza zawierajacego [wzorzecl) 
do wiersza zawierajacego (wzorzecz) (lacznie z tymi wierszami). ( Wzorzec) oznacza 
(wyrazenie) badz (wyrazenie-regularne) . 

Wzorce BEGIN i END nie mogq bye czesciq wzorca zlozonego. Podobnie czescia^ 
wzorca zlozonego nie moze bye wzorzec z przecinkiem. 

2.1. BEGIN/END 

Wzorzec BEGIN nie pasuje do zadnego wiersza z pliku wejsciowego, a odpowiadaj^- 
ca mu akcja jest wykonywana przed przeczytaniem przez AWK pierwszego znaku 
z tego pliku. Podobnie instrukeje wzorca END wykonywane sq po przeczytaniu 
wszystkich znakow pliku wejsciowego. Mozliwe jest umieszczenie wielu wzorcow 
BEGIN i END w programie AWK-owym; sq one wtedy wykonywane po kolei. Zwy- 
czajowo wzorce BEGIN sq umieszczane na poczatku a END na kohcu pliku. 

Jednym z najczestszych sposobow uzycia BEGIN jest zmiana domyslnego 
sposobu w jaki AWK dzieli wiersze z czytanego pliku na pola. Wbudowana zmienna 
FS definiuje napis-separator pol w rekordzie. Domyslnie pola oddzielone sq, znakami 
spacji lub/i tabulacji (FS=" "). Przy uruchomieniu programu zawierajacego tylko 
wzorce BEGIN AWK nie oczekuje w linii poleceh nazwy zadnego pliku wejsciowego, 
por. przyklad 17 (s. 35). 

2.2. Wyrazenie 

Wzorcami moga^ bye wyrazenia arytmetyczne lub napisowe. Odpowiednia (akcja) 
jest wykonywana za kazdym razem gdy takie (wyrazenie) ma wartosc rozn^ od 
zera lub od napisu pustego. Przykladowo: 

$(NF-4) == "Fra" { print $0 } # Wydrukuj wina francuskie 

w powyzszym wierszu, ktory jest kompletnym programem AWK-owym, wyraze- 
niem we wzorcu jest porownanie czwartego pola od konca wiersza z napisem "Fra" . 
Jezeli napisy s^ identyczne to wartosciq wyrazenia jest prawda i AWK wykonuje 
akcj^ - drukuje caly wiersz. Zgodnie z tym co juz powiedziano, tego typu akcja jest 
wykonywana domysnie - jezeli nie podano innej, zatem program mozna zapisac 
jeszcze krocej: 

$(NF-4) == "Fra" # Wydrukuj wina francuskie 

Przykladem wzorca zawierajacego wyrazenie arytmetyczne moze bye wydru- 
kowanie tych gatunkow win, na ktory cli obrot byl wi^kszy od 10 tys. zl: 

$NF * $(NF-1) > 10000 # Wydrukuj najlepiej kupowane 

UwAGl: Znak # rozpoczyna komentarz - wszystko od znaku # do kohca wiersza 
jest przez AWK ignorowane. 



2. Wzorce 11 



2.3. Wzorzec regularny 

Wzorzec regularny to wyrazenie regularne ujete w par§ znakow /. Podstawowe 
sposoby uzycia wzorca regularnego to: 

/(r)/ 

Pasuje do biezacego wiersza z pliku wejsciowego jezeli zawiera ona podnapis 

pasujacy do wyrazenia regularnego (r). 

[wyrazenie] ~ / \r) / 

Pasuje do napisu bedacego wartoscia^ {wyrazenia) jezeli zawiera on podnapis 
pasujacy do wyrazenia regularnego (r). Zapis / \r) / jest rownowazny formie 
$0 " /(r)/. 

{wyrazenie) !~ /(r)/ 

Pasuje do napisu bedacego wartoscig, {wyrazenia) jezeli nie zawiera on podnapisu 

pasujacego do wyrazenia regularnego (r). 

Wyrazenia regularne moga^ bye pomocne w rozwiazaniu problemu wydrukowa- 
nia win wytrawnych i polwytrawnych: 

$(NF-2) ~ /wytrawne/ # wydrukuj cos wytrawnego 

AWK wydrukuje kazdy wiersz, ktorego trzecie od koiica pole zawiera napis 
wytrawne. W tym przykladzie zysk jest niewielki, zamiast $(NF-2) "/wytrawne/ 
mozna zapisac: 

$(NF-2) == "wytrawne" # wydrukuj wytrawne 
$(NF-2) == "polwytrawne" # ... i polwytrawne 

ale w ogolnym przypadku wyrazenia regularne potrafi^ znakomicie ulatwic prac§. 



2.4. Wzorzec zlozony 

Wzorzec zlozony to wyrazenie zlozone z wzorcow i operatorow logicznych I I , &&, ! . 
Wzorzec zlozony pasuje do biezacego wiersza z pliku wejsciowego jezeli wartoscia^ 
wyrazenia jest prawda (czyli jest niezerowa lub niepusta). Ponizszy przyklad: 

$(NF-2) == "wytrawne" II $(NF-2) == "polwytrawne" # cos wytrawnego 

pokazuje wzorzec zlozony i jednoczesnie potwierdza, ze najlepiej do rozwiqzania 
problemu drukowania win wytrawnych korzystac z wyrazeii regularnych. 

Inny przyklad wzorca zlozonego pozwoli nam rozwiazac problem wydrukowania 
win francuskich, na ktorych obrot byl wiekszy od 10 tys. zl: 

$(NF-4) == "Fra" && $(NF-1) * $NF > 10000 



12 3. Wyrazenia regularne 



2.5. Wzorzec z przecinkiem 

Pasuje do wszystkich wierszy, od wiersza pasujacego do {wzorcal) do wiersza 
pasujacego do (wzorcaz) (lacznie z tymi wierszami). Jezeli w pliku po raz kolejny 
pojawi sie^ {wzorzecl), to znowu pasuje wszystkie wiersze az do napotkania 
(wzorcaz) . Jezeli AWK nie znajdzie \wzorca2) , to pasujq, wszystkie wiersze az do 
kofica pliku. Jezeli w pliku nie ma [wzorcal) , to nie pasuje zaden wiersz z tego pliku. 
Przykladowo wykonanie ponizszego programu spowoduje wydrukowanie wierszy 
o numerach od 4 do 14 z pliku tdf2000.txt: 

$3 ~ /Briancon/, $3 " /Morzine/ 

Jezeli wykonamy nastejiujacy program: 

$3 ~ /Draguignan/ , $3 ~ /Briancon/ 

to powstaje pytanie czy na wydruku otrzymamy tylko jeden wiersz (zawiera 
"Draguignan" i jednoczesnie zawiera "Briancon") czy tez szesc wierszy (od 4 
do 9; dziewiaty tez zawiera slowo "Briancon") z pliku tdf2000.txt? Otoz AWK 
po sprawdzeniu, ze wiersz pasuje do {wzorcal) sprawdza ten sam wiersz, czy aby 
nie pasuje on do (wzorca2), co powoduje, ze w takim wypadku drukowany jest 
tylko ten wiersz. Gdyby AWK dzialal tak jak program sed, tj. po dopasowaniu 
wiersza do {wzorcal) , dopasowywal {wzorzecz] do nastejmego i kolejnych wierszy 
wtedy na wydruku pojawiloby si§ 6 wierszy. 

3. Wyrazenia regularne 

Wyrazenia regularne to wyrazenia umozliwiajace specynkowanie Mas napisow. 
O napisie nalezacym do tej klasy mowimy, ze pasuje do wyrazenia regularnego. 
Wyrazenia regularne sq konstruowane z nastepujacych elementow: „normalnych 
znakow" (wszystkie litery, cyfry, wi^kszosc pozostalych znakow) oraz metaznakow 
\, ", $, ., [, ] , I, (,),*,+,?. Ponizsza tabela przedstawia poszczegolne elementy 
wyrazeii regularnych, wedlug malejacej kolejnosci wykonywania: 

Wyrazenia regularne 

Wyrazenie Znaczenie 

((r)) (r) (nawiasy sluzq do grupowania wyrazeii) 

c znak nie b^dqcy metaznakiem 

\c znak sterujacy albo znak/metaznak c 

poczatek napisu 
$ koniec napisu 

dowolny znak 
[ab. . .] dowolny ze znakow a, b... 

[~ab. . .] dowolny ze znakow oprocz a, b... 

(r)* zero lub wiecej powtorzeii (r) 

(r)+ jedno lub wiecej powtorzenie (r) 



3. Wyrazenia regularne 13 



(r)? zero lub jedno powtorzenie (r) 

\rl) | \rz) \rl) lub \rz) ((r) oznacza wyrazenie regularne) 

Do grupy znakow wewnatrz nawiasow klamrowych pasuje jeden dowolny znak 
z tej grupy. Wewnatrz nawiasow klamrowych wszystkie znaki oprocz \, - i tracg, 
swoje metaznaczenie. Przykladowo: [. . .] oznacza trzy kropki a nie trzy dowolne 
znaki. 

Zapis [a-z] oznacza zakres czyli jeden znak od a do z, przy czym obo- 
wiazuje kolejnosci kodow ASCII. Zatem specyfikacja [0-9] jest rownowazna 
[0123456789], zas [A-Da-d] oznacza [ABCDabcd] . Jezeli znak - jest pierwszym 
znakiem w grupie, wtedy jest traktowany literalnie, tj. [-+] oznacza albo minus 
albo plus podczas gdy [+-] jest bledem - AWK oczekuje kofica zakresu znakow. 

Dopelnieniem grupy lub zakresu znakow jest grupa lub zakres poprzedzona 
znakiem " (bezposrednio po otwierajacym nawiasie [). Przykladowo specyfikacja 
[~0-9] oznacza jeden dowolny znak ale nie cyfr^; [~A-ZACIp.N0SZZ] dowolny znak 
nie bedacy duzq, literq. Znak " jest traktowany literalnie jezeli nie rozpoczyna 
grupy. Na przyklad "["-] pasuje do kazdego znaku oprocz znaku ~ na poczatku 
napisu. 

Nawiasy okrqgle sluzq do grupowania i - podobnie jak w wyrazeniach 
arytmetycznych - posiadajfj najwyzszy priorytet wykonania. Przykladowo: 

/ (Ali I ali) (baba I gator) / 

pasuje do nast^pujacych napisow: "Alibaba", "Aligator", "alibaba", oraz 
"aligator". Zwrocmy uwag^, ze poniewaz operator I ma najnizszy priorytet 
wykonywania mozemy pisac (Ali I ali) a nie ((Ali) I (ali)). 

Znaki sterujace, zapisujemy w konwencji j^zyka C. Sq to: \a (dzwonek, alarm), 
\b (znak cofni^cia, backspace), \f (znak konca strony, form feed), \n (przejscie 
do nowego wiersza, new line), \r [carriage return), \t (znak tabulacji). Ponadto 
znak \\ oznacza \, zas kazdy znak mozemy zapisac przy pomocy kodu osemkowego 
uzywaj^c konwencji \\cyfra) \cyfra) \cyfra) . 

PRZYKLAD 2 

Do wyrazenia regularnego /"[ \t]*$/ pasuje wszystkie napisy skladajace si§ 
tylko ze znakow spacji, tabulacji i napisu pustego. Do /"[" \t]*$/ pasuje 
wszystkie napisy oprocz skladajacych si^; ze spacji, znakow tabulacji i pustych. 
Z kolei do /[+-]? [0-9] + [.]? [0-9] */ pasujq wszystkie liczby rzeczywiste ze 
znakiem. □ 

AWK zawsze dopasowuje do wyrazenia regularnego najdluzszy z mozliwych 
napisow, rozpoczynajac dopasowywanie od lewej strony, tak szybko jak to jest 
mozliwe. Poszczegolne operatory powtorzeh (+, *, ?) dopasowuje napis tak dlugo 
jak to jest mozliwe. Przykladowo niech plik zawiera nastejiujacy krotki tekst: 

<tr align="left"Xtd>Chardonnay Lyngrove</tdXtd>RPA</tdx/tr> 

Jaki napis zostanie dopasowany do wyrazenia regularnego /< . +>/? Na pierwszy 
rzut oka mogioby si§ wydawac, ze <tr align="left">, ale nie jest to prawdq: 



14 3. Wyrazenia regularne 



dopasowany zostanie caly wiersz, gdyz jest to najdluzszy z mozliwych pasujacych 
napisow zaczynajacych siig od < a koiiczacych >. 

Rozpatrzmy kolejny przyklad. Jaki napis zostanie dopasowany do wyrazenia: 
/C*/? Odpowiedz, ze napis rozpoczynajacy si§ od C w slowie Chardonnay do konca 
wiersza jest bledna. AWK dopasuje si§ jedynie do napisu o zerowej dlugosci na 
poczatku wiersza. Tak sie^ stanie poniewaz powtorzeri C jest poprawnym punktem 
startu a kolejny znak nie jest juz duzq liters C - dopasowywanie jest konczone 
z wynikiem C. 

PRZYKLAD 3 

Rozwazmy z kolei jak mozna z pliku tdf2000.txt wydrukowac szczyty o roznicy 
poziomow wiekszej od 1 km. Nie mozna po prostu napisac $(NF-1) > 1000 
poniewaz nie wszystkie rekordy zawierajq w przedostatnim polu roznicy poziomow. 
Na przyklad w pierwszym wierszu przez zupelny przypadek $(NF-1) jest rowne 
3630. Zresztg, napotkanie pustego wiersza spowoduje blad fatalny i przerwanie 
wykonywania programu poniewaz w AWK nie wolno odwolywac sig do ujemnych 
numerow pol. Oczywistym rozwiazaniem jest wyspecyfikowanie akcji dla kazdego 
rodzaju rekordu: naglowka, naglowka etapu, gory i pustego: 

NR==1, /"-+[ \t]*$/ { next } # Pomin naglowek, 
NF < 1 { next } # puste wiersze 

/~Etap[ \t]+[0-9]+:/ { next } # oraz naglowki etapow 
$(NF-1) > 1000 # Wydrukuj podjazd o roznicy > 1000 

Wykorzystana w powyzszym programie, jeszcze nie omawiana, instrukcja next 
(por. punkt 8) powoduje: przerwanie wykonywania programu, wczytaniu nast^p- 
nego rekordu ze strumienia danych wejsciowych a nastepnie rozpoczecie wykony- 
wania programu od poczatku, tj. od pierwszej pary wzorzec-akcja. 

Kluczowe znaczenie w powyzszym programie ma kolejnosc poszczegolnych 
par wzorzec-akcja, dzieki ktorej wzorzec $(NF-1) > 1000 „widzi" tylko wiersze 
z danymi o gorach, pozostale zas sq, po drodze „odcedzane" . D 



3.1. Napisy jako wyrazenia regularne 

Zwykle wyrazenia regularne zapisywane s^ jako ciqgi znakow umieszczone pomie;- 
dzy znakami ciachow. Mozliwe jest tez uzywanie napisow jako wyrazeii regular- 
nych. Wsz^dzie tam gdzie AWK oczekuje pojawienia si§ wyrazenia regularnego 
(jak, np. po prawej stronie operatorow i ! ~) umieszczone tam wyrazenie zosta- 
nie przeksztalcone a nastepnie zamienione na napis, ktory b^dzie interpretowany 
jako wyrazenie regularne. Przykladowo, ponizszy program: 

BEGIN {cyfry="-[0-9]+$" >; $0 "cyfry 

wydrukuje wszystkie wiersze, ktore zawieraj^ wylacznie liczbe; calkowit^ (bez 
znaku) . 



4. Wyrazenia 15 



Poniewaz wyrazenia napisowe mogq bye laczone (por. punkt 4.6), wyrazenie 
regularne moze bye konstruowane dynamicznie z czesci skladowych. Przykladowo 
ponizszy program: 

BEGIN {znak ="[-+]?"; cyf ra=" [0-9] +" ; liczba = "~" znak cyfra "$"} 
$0 ~ liczba 

moze sluzyc do wydrukowane wszystkich wierszy z pliku, ktore zawierajq wylqcznie 
liczbe; calkowita^ ze znakiem: 

UwAGl: Jezeli wewnatrz wyrazenia regularnego wystepuje literalnie jakis metaznak 
to nalezy go poprzedzic dwoma a nie jednym znakiem \. Przykladowo, program: 

$0 ~ /~\\*[0-9]+\\*$/ 

wydrukuje wiersze zawierajace liczbe; calkowita^ bez znaku otoczonq, znakiem *. 



4. Wyrazenia 

Podstawq skladni wyrazen AWK jest skladnia wyrazeri jezyka C wzbogacona 
o operacje tekstowe. Elementami wyrazeri sq: stale, zmienne, operatory, funkeje 
wbudowane i definiowane przez uzytkownika oraz elementy tablic asocjacyjnych. 

4.1. Stale 

W AWK istniejq tylko dwa typy danych: liczbowy i napisowy. Stale liczbowe 
zapisujemy jak w C, tj. 3 . 1415 lub 1 . 333e-5, stale napisowe otacza sie^ znakami " . 
Stale napisowe moga^ zawierac znaki sterujace takie jak \n czy \f . 

4.2. Zmienne 

W skladni AWK wyrozniamy zmienne: wbudowane, zdefmiowane przez uzytkow- 
nika i przechowujace zawartosci pol. Nazwy zmiennych definiowanych przez uzyt- 
kownika mogq skladac sig z liter, cyfr i znaku podkreslenia. Pierwszym znakiem 
nazwy nie moze bye cyfra. Nazwy zmiennych wbudowanych skladaj<| si§ wylqcznie 
z duzych liter alfabetu. Nazwy zmiennych przechowujacych zawartosci pol zaczy- 
naja^ sie; od znaku $, po ktorym wystepuje liczba (ogolnie: wyrazenie). 

Zmienne liczbowe przechowujq wartosci zmiennopozycyjne, przy czym ich 
dokladnosc zalezna jest od implementacji. Zmienne napisowe przechowujq ciqgi 
znakow (napisy). Zmiennych nie deklaruje si^. Typ zmiennej okreslony jest 
przez kontekst; w razie potrzeby zawsze dokonywana jest odpowiednia konwersja. 
Zmienna nie zainicjowana ma wartosc zero lub "" (napis pusty). 

Interpretacja wyrazeh numerycznych i tekstowych w operacjach logicznych 
jest nastepujaca: falsz odpowiada liczbie i napisowi pustemu "", zas prawda 
odpowiada wszystkim innym liczbom i napisom. 



16 



4. Wyrazenia 



4.3. Zmienne wbudowane 

Zmienne wbudowane sa^ dokladnie opisane przy okazji omawiania tych aspektow 
AWK, ktorych dotycza; 

Zmienne wbudowane 

Zmienna Opis znaczenia 

ARGC liczba argumentow wywolania programu 

ARGV tablica argumentow wywolania programu 

ARGIND* indeks w ARGV odpowiadajacy biezacemu plikowi 

ENVIRON* tablica zmiennych srodowiskowych 

ERRNO* napis z systemowym opisem bledu 

FIELDWIDTHS* specyfikacja dlugosci pol, por. punkt 11.4 

FILENAME nazwa biezacego pliku wejsciowego 

FNR numer biezacego rekordu w biezacym pliku 

FS separator pol 

IGNORECASE* przelacznik rozrozniania wysokosci liter 

NF liczba pol w biezacym rekordzie 

NR liczba przeczytanych rekordow 

OFMT format wydruku argumentow numerycznych funkcji print 

OFS separator pol na wyjsciu, por. punkt 12.2 

ORS separator rekordow na wyjsciu, por. punkt 12.2 

RLENGTH por. opis funkcji match w punkcie 6 

RS separator rekordow 

RT* napis pasujacy do wyrazenia RS, por. punkt 11.2 

RSTART por. opis funkcji match w punkcie 6 

SUBSEP separator indeksow tablic, por. punkt 9.2 



ENVIRON* jest tablic^ zawierajaca^ wartosci zmiennych srodowiskowych przy 
czym indeksami sa^ nazwy zmiennych. Przykladowo: 

gawk 'BEGIN {print ENVIRON ["HOME"] >' 

zawiera np. /home/tomek. 

ERRNO* zawiera napis z systemowym komunikatem o bledzie, jezeli przy 
wykonaniu funkcji getline lub close wyst^pi blqd. 

IGNORECASE* okresla czy AWK rozroznia duze i male litery przy porownywaniu 
napisow i wyrazen regularnych. Jezeli IGNORECASE* jest niezerowe lub niepuste, 
wtedy operatory ~, ! ~ i funkcje gensub*, gsub, index, match, split oraz sub nie 
rozrozniajq duzych i malych liter. Dotyczy to takze wartosci zmiennych RS i FS. 
Poczawszy od wersji 3.0, gawk obsluguje norm^ ISO-8859-1 (Latin-1). Standard 
ten nie zawiera jednak wiekszosci polskich znakow diakrytycznych. 

ARGIND* przechowuje indeks, pod ktorym w tablicy ARGV znajduje si§ nazwa 
przegladanego pliku. Zawsze jest prawdziwa rownosc FILENAME == ARGV [ARGIND] . 

UwAGl: Zmienna FILENAME zawiera nazw§ biezacego pliku wejsciowego. Oznacza 
to, ze w obrebie wzorcow BEGIN i END wartosc FILENAME jest nieokreslona. 



4. Wyrazenia 17 



4.4. Zmienne przechowuj^ce zawartosci pol 

Zmienne przechowujace zawartosci pol mogq, bye wykorzystane wewnatrz wyrazeii, 
mozna takze im nadawac wartosci. Jezeli wartosc zmiennej $0 zostala zmodyfiko- 
wana przez podstawienie lub zamiane^ (np. funkejq sub) to wartosci zmiennych $1, 
$2, ... oraz zmiennej NF sq powtornie wyznaczane. Podobnie jezeli zmodyfikowano 
ktorakolwiek ze zmiennych $1, $2, ..., wtedy wartosc zmiennej $0 jest odtwarzana 
(przy wykorzystaniu wartosci zmiennej OFS jako separatora pol). 

Numery pol mogq, bye wyznaczane jako wartosci wyrazen. Przykladowo 
$(NF-1) oznacza przedostatnie pole w biez<|cym rekordzie (nawiasy sq, istotne, 
poniewaz $NF-1 oznacza wartosc pola $NF pomniejszonq o 1). Mozliwe jest 
takze przypisanie wartosci zmiennym odpowiadaj<|cym polom nie istniejacym 
w czytanym pliku. W takim wypadku pole zostanie utworzone i przypisana 
zostanie odpowiednia wartosc. Odwolanie si§ do pola o numerze ujemnym jest 
bledem koiiczqeym dzialanie programu. 

4.5. Operatory arytmetyczne 

Ponizsza tabela zawiera zestawienie wszystkich operatorow arytmetycznych. 

Operatory arytmetyczne 

Operator Opis znaczenia 

* iloczyn 

+ suma 

roznica 
/ iloraz 

% modulo 

pot^ga 
++ inkrementacja 

dekrementacja 

Operator modulo to reszta z dzielenia calkowitego, tj. 5.1 % 3 == 2.1 ma 
wartosc prawda. 



4.6. Operatory napisowe 

Napisy i zmienne napisowe mozna laczyc (konkatenowac) przy pomocy „niewi- 
docznego" operatora - po prostu nalezy umiescic napisy obok siebie. Przykladowo 
po wykonaniu: 

y = "Ali"; z = "gator"; xl = y "ba" "ba"; x2= y z; 

zmienna xl ma wartosc „Alibaba"; zmienna x2 ma wartosc „Aligator". Oprocz 
operacji konkatenacji AWK nie ma zadnych innych operatorow napisowych. 



18 4. Wyrazenia 



4.7. Operatory porownywania i operatory logiczne 

Zapis i dzialanie operatorow w AWK w wypadku zmiennych typu liczbowego jest 
identyczny jak w jezyku C. Nowoscia^ AWK jest to, ze mogq bye takze stosowane 
do napisow. 

Operatory porownywania 

Operator Opis znaczenia 

== rowne 

! = rozne 

< nmiejsze 

<= nmiejsze lub rowne 

> wi^ksze 

>= wi^ksze lub rowne 

Napisy sq porownywane wedlug kodow ASCII w taki sposob, ze najpierw 
porownywane sa^ pierwsze znaki, potem drugie itd. Przykladowo: " 10" jest nmiejsze 
od "9". Jezeli jeden napis jest przedrostkiem drugiego to krotszy napis jest 
mniejszy od dluzszego, np. "Ali" jest mniejsze od "Alibaba". 



Operatory logiczne 


Operator 


Opis znaczenia 


&& 


iloczyn 


II 


suma 


i 


zaprzeczenie 



PRZYKLAD 4 

Zaimplementujmy funkeje;, zwracaj^c^ 1 jezeli rok jest przest^pny, lub dla lat 
nieprzestejinych. Algorytm cytujemy za [-■], s. 121. 

function leapyear (year) { 

return year %4 == && year % 100 \ 
! = I I year%400 ==0 ; } 

BEGIN {print leapyear (1996) , leapyear (1806) , 
leapyear ( 1066) } 

Wzorzec BEGIN jest potrzebny tylko dla testowania funkeji. Jezeli powyzszy kod 
umiescimy, np. w pliku lyear.awk to piszqc awk -f lyear.awk otrzymamy na 
ekranie: 1 0. □ 

UwAGl: Bardzo dluga instrukeja moze zostac podzielona i zapisana w kilku 
wierszach. Znakiem kontynuacji jest \, bezposrednio przed znakiem koiica wiersza 
(por. drugi wiersz przykladu). Jezeli wiersz konczy si^ przecinkiem (por. wiersz 
przedostatni) to znak kontynuacji jest opcjonalny. 



4. Wyrazenia 19 



4.8. Operatory pasowania do wyrazen regularnych 

W skladni AWK sq dwa takie operatory ~ oraz ! ~. Umozliwajq one dopasowanie 
zmiennej do wyrazenia regularnego. Przykladowo $1 "/Chardonnay/ jest praw- 
dziwe, gdy pierwsze pole zawiera napis Chardonnay. Samotnie pojawiajace sie; 
wyrazenie /Chardonnay/ jest rownowazne $0 ~ /Chardonnay/. Operator !~ pa- 
suje do dopelnienia wyrazenia regularnego, tj. $1 ! "/Chardonnay/ jest prawdziwe 
gdy $1 nie zawiera napisu Chardonnay. 

4.9. Przypisanie 

Przypisanie oznaczane jest w AWK pojedynczym znakiem rownosci =. Podobnie 
jak w jezyku C operator ten nadaje zmiennej wartosc i zwraca przypisanq wartosc, 
stad dozwolone sq wyrazenia postaci x = y = 1 lub (x = y) <= 1. 

Z operatorem przypisania zwiazane s^ operatory modyfikacji: +=, -=, *=, /=, 
7o=, /= i "=. Przykladowo wyrazenie x += y jest tozsame z x = x + y, wyrazenie 
x -= y jest tozsame z x = x - y itd. 

PRZYKLAD 5 

Dla danych z pliku wina.txt, obliczyc obrot dla win bialych i czerwonych oraz 
obrot laczny. Zadanie to rozwiazuje nastepujacy program: 

# ! /usr/bin/awk -f 

{obrot = $NF * $(NF-1); oo += obrot } 
$(NF - 3) " /Male/ {biale += obrot } 
$(NF - 3) /czerwone/ {czerwone += obrot } 
END { print "Obrot razem:", oo ", w tym: czerwone:", 
czerwone ", biale:", biale "."; } 

Zwrocmy uwage; na instmkcje; print. Czesc argumentow jest oddzielona przecin- 
kiem a czesc nie. Jest to dopuszczalne poniewaz argumenty oddzielone odstejia- 
mi sq konkatenowane (liczby przed konkatenacjq, sq konwertowane do napisow) 
i „z punktu widzenia" instrukcji print stanowi^ jeden napis. Natomiast argumen- 
ty oddzielone przecinkami sq na wydruku oddzielone odstejiem. D 

4.10. Operator warunkowy ?: 

Operator warunkowy ?: posiada nastepujacy skladni^: 

(wyrazeniel) ? [wyrazenie2j : (wyrazenieS) 

Najpierw obliczane jest (wyrazeniel) . Jesli jest ono prawdziwe obliczane jest 
(wyrazeniez) , w przeciwnym wypadku (wyrazenieS). 

Ponizszy program oblicza i drukuje odwrotnosc pierwszych pol wszystkich 
rekordow, sprawdzajac czy $1 nie jest rowne zeru: 

{print $1!=0 ? 1/$1 : "Zero w wierszu" , NR;} 



20 6. Napisowe funkcje wbudowane 



5. Arytmetyczne funkcje wbudowane 

AWK posiada inny zestaw funkcji wbudowanych niz jezyk C. Funkcje wbudowane 
mogq bye, bez zadnych ograniczeii, elementami wyrazeri. Oto lista takich funkcji 
(niech \x), (y) bedq pewnymi wyrazeniami): 

Funkcje arytmetyczne 

Funkcja Wartosc funkcji 

atan2((y) , (i)) arcus tangens z y/x w zakresia — it do n 

cos ((a;)) cosinus z x, x w radianach 

sin((x)) sinus z x, x w radianach 

exp((a;)) eksponent, czyli funkcja wykladnicza e x 

int ( \x) ) czesc calkowita z x 

log ((a:)) logarytm z x przy podstawie e 

sqrt((a;)) pierwiastek kwadratowy z x 

randO przypadkowa liczba z przedzialu (0, 1) 

srand((a;)) x wartosc poczatkowa dla generatora liczb pseudolosowych 

Uzywajac powyzszych funkcji mozna uzyskac uzyteczne liczby, na przyklad 7r 
lub e: atan2(0, — 1) = 7r oraz exp(l) = e. Rowniez uzyskanie logarytmu dziesietaego 
nie jest problemem, jesli zastosujemy wzor log(x)/ log(10). Natomiast poprzez pod- 
stawienie randint = int (n * randO) + 1 nadajemy zmiennej randint wartosc 
pseudolosowq z przedzialu (l,n). 



6. Napisowe funkcje wbudowane 

Ponizsze zestawienie zawiera funkcje AWK umozliwiajace manipulowanie napisa- 
mi. W zestawieniu (r) oznacza wyrazenie regularne, (s) i (t) napis. 

Funkcje napisowe 

g B ub((r), («),(*)) 

Zamienia wszystkie napisy pasujace do wyrazenia regularnego (r) na napis 
(s) w napisie (t) . Zwracana jest liczba zamian. Parametry (r) i (s) mog^ 
bye w ogolnosci wyrazeniami: paramter \t) musi bye zmienna^ lub elementem 
tablicy, tak aby AWK mogi gdzies przypisac zmodyfikowana^ wartosc. Jezeli gsub 
wywolamy tylko z dwoma pierwszymi parametrami, to zmiany dokonywane s^ 
w napisie $0 (tj. gsub((r) , (s)) jest rownowazne gsub((r) , (s) ,$0)). Znak & w (s) 
oznacza napis, ktory zostal dopasowany do (r). Przykladowo po uruchomieniu: 

awk 'BEGIN {$0 = "baba bababa" ; gsub(/(ba)+/, "WW&W&&") ; print } ' 

zostanie wydrukowany napis " \baba&baba \bababa&bababa" . Poniewaz wewnatrz 
napisow znak \ musi bye zdublowany zeby AWK traktowal go literalnie, dlatego 
"\\&" oznacza &. Zas wstawienie \ osiqgniemy za pomoca^ "WW" (cztery \!). 



6. Napisowe funkcje wbudowane 21 



gensub ((r),(s), (a), (i))* 

Uogolniona funkcja gsub. Zwraca zmieniony napis (nie modyfikuje oryginalnego 
napisu (t)\). Zamienia napisy pasujace do wyrazenia regularnego (r) na (s) 
w oparciu o (a), w (t) (jezeli nie ma (t), domyslnym argumentem jest $0). 
Argumenty (s), (a) oraz (t) s^ napisami. Argument (a) okresla, ktory z kolei 
podnapis pasujacy do wyrazenia (r) ma bye wymieniony. Jezeli (a) jest napisem 
rozpoczynajacym si§ od "g" (lub "G") to wymieniane s^ wszystkie napisy 
pasujace do (r). Funkcja gensub* umozliwia wstawienie do (s) fragmentow napisu 
dopasowanego do (r) . Jezeli wyrazenie (r) podzielimy za pomocq nawiasow, ( i ) 
na czesci skladowe to te skladowe mog^ pozniej pojawic si§ w (s) (oznaczamy 
je jako \(n), gdzie (n) jest cyfr^ od 1 do 9). Znaczenie tego jest takie, ze 
napis dopasowany do (n)-tego fragmentu jest kopiowany do napisu zwracanego 
przez funkcje^ W efekcie mozliwe sq wszelkiego rodzaju zmiany kontekstowe, 
por. przyklad 8 (s. 23). Symbol \0 oznacza napis dopasowany do calego wyrazenia 
regularnego (r) (to samo znaczenie ma &). Przykladowo wykonanie programu: 

awk 'BEGIN {$0 = "12, 122, 1,901, 20,500, 102,153,000"; 
$0 = gensub (/( [0-9]), ([0-9])/, "\\1\\2" , "g") ; print }' 

spowoduje wydrukowanie napisu "12, 122, 1901, 20500, 102153000". Znaki 

& i \ s^ wewnatrz (s) specjalne; ich znaczenie podano w opisie funkeji gsub. 

W szczegolnosci nalezy uzywac sekwencji \\ do wstawienia w napisie znaku \ 

literalnie. 

Ponizszy przyklad wyjasnia znaczenie argumentu (a): 

BEGIN{ t = "Alibababa"; print gensub(/ba/, "BA" , 2, t) } 

otrzymamy: AlibaBAba 

index ((s) , (i)) 

Zwraca numer pierwszego znaku napisu \t) w napisie (s). Jezeli (s) nie zawiera \t) 

zwracana jest wartosc zero. Pierwszy znak w napisie ma numer 1. Przykladowo: 

index ( "Alibaba" , "baba") zwraca 4; 

length ((s)) 

Podaje dlugosc napisu (s). 

match((s) , (r)) 

Jezeli (s) zawiera podnapis pasujacy do (r), to zwraca numer pierwszego znaku tego 

podnapisu; w przeciwnym razie zwracane jest 0. Ponadto nadawane sg, wartosci 

zmiennym RSTART oraz RLENGTH. RSTART jest rowne wartosci zwracanej przez 

funkcja, RLENGTH jest rowne dlugosci podnapisu pasujqeego do (r). 

split ((s), (a), (fs)) 

Z napisu (s) tworzy tablic^ napisow (a) w oparciu o (fs). Argument \fs) jest 

wyrazeniem regularnym. Jezeli split wywolamy tylko z dwoma parametrami to 

napisem separujacy jest wartosc zmiennej FS, czyli separator pol w rekordzie. 

spr intf ( (format) , (lista-wyrazen) ) 

Zwraca napis, sformatowany wedlug napisu (format), por. funkcja printf , w punk- 

cie 12. 



22 6. Napisowe funkcje wbudowane 



B ub((r), (*),(*)) 

Funkcja dzialajaca jak gsub, ale wymieniajaca tylko pierwsze wystapienie napisu 

pasujacego do (r) na napis (s) . Zwracana jest liczba zamian. 

substr ((s) ,(p) ,(n)) 

Zwraca napis wyciety z (s) poczawszy od pozycji (p) o dlugosci (n) znakow (lub 

do koiica (s), jezeli ostatni argument jest pominiety). Przykladowo wykonanie 

instrukcji: 

print substr ("Alibaba" ,4) ; 

spowoduje wydrukowanie napisu "baba". 

tolower((s))* 

Zwraca napis, w ktorym duze litery zostaly zamienione na male. W wypadku 

polskich tekstow funkcja ta ma ograniczone zastosowanie, nie zamieni bowiem 

liter z gornej polowki tabeli ASCII, gdzie znajdujfj si§ 4: C, I£, itd. 

toupper((s))* 

Zwraca napis, w ktorym male litery zostaly zamienione na duze. Z „polskiego" 

punktu widzenia ma t^ samq wad§ co tolower*. 

PRZYKLAD 6 

Problemem przy wymianie danych pomi^dzy DOS-em a Uniksem jest stosowanie 
innych sekwencji znakow do oznaczania konca wiersza. Ponizszy program dokonuje 
odpowiedniej konwersji: 

#! /bin/bash 

cat $* I awk '{gsub(/\r/, "") ; print $0}' 

Wiele uniksowych implementacji AWK zle interpretuje poprawne programy 
w przypadku gdy przetwarzany plik ma DOS-owe konce wiersza. Na takq, oko- 
licznosc warto zapamietac powyzszfj, funkcja gsub. □ 

PRZYKLAD 7 

Ponizsza funkcja (porownaj punkt Funkcje dalej w tekscie) realizuje kontekstowa^ 
zamian^ frazy na fraz^. Jest namiastk^ tego czego AWK-owi do tej pory brakowalo 
(por. nast^pny przyklad) - zamiany wyrazenia regularnego na wyrazenie regularne. 

# wymiefi w (s) (co) w kontekscie (bef) (aft) na (na) 
function exch (bef, co, aft, na, s) { 
while (match(s, bef co aft) > 0) { 
match(s, bef co aft); 

s = substr (s, 1, RSTART) na substr (s, RSTART+RLENGTH-1) ; } 
return s ; 
} 

Zakladamy, ze (bef) jest jednym znakiem i poprzedza (co) . Nastejmjace po (co) 
(aft) tez jest jednym znakiem. Przykladowo uruchomienie programu: 



6. Napisowe funkcje wbudowane 23 

{ s = exch(" [0-9] ","-", "[0-9]", " — ", $0); print s } 

z podaniem jako argumentu pliku kloss.txt: 

Dzialalnosc agenta J-23 (Hans-Peter Kloss) w latach 1941-1945 
opisano na stronach 1234-1239. 

wymieni w nim wszystkie frazy (cy fra) - (cyfra) na (cyfra) — (cyfra), pozostale znaki 
„-" zostang, niezmienione. □ 

PRZYKLAD 8 

Ponizszy program wykorzystujacy funkcje gensub*: 

{ $0=gensub(/([0-9])-([0-9])/,"\\l— \\2" , "g" ,$0) ; print } 

dziala identycznie jak funkcja exch, czyli zamienia w calym tekscie wszystkie frazy 
(cyfra) -(cyfra) na (cy fra) — (cyfra) , np. 1234-1239 zmieni na 1234—1239. D 

PRZYKLAD 9 

Ponizsza funkcja jest odpowiednikiem funkcji toupper*; ma t§ zalet§, ze „roz- 
poznaje" polskie znaki. Latwo tez daje si§ modyfikowac dla roznych wariantow 
kodowania polskich znakow. 

function upper (string, i, j){ 

newstring = "" 

for (i = 1; i <= length ( string) ; i++){ 
char = substr (string, i, 1) 
for (j = 1; j <= ALPHABET; j++){ 
if (char == little [j]){ 

char = big[j]; j = ALPHABET + 1; } 
} 

newstring = newstring char; 
} 

return newstring 
} 

BEGIN{ LOWER = "abcdef ghijklmnopqrstuvwxyzac^lnoszz" ; 
UPPER = "ABCDEFGHIJKLMN0PQRSTUVWXYZ4CELNDSZZ"; 
ALPHABET = length(LOWER) ; 

for (i = 1; i <= ALPHABET; i++){ 
little [i] = substr(LOWER,i,l) 
big[i] = substr(UPPER,i,l) } } 

Dodajmy jeszcze nastejmjacy wzorzec BEGIN: 

BEGIN { print upperC'Pod zdzbLEm Zolw spal sni^TY."); } 

Po umchomieniu otrzymamy na ekranie: POD ZDZBLEM ZOLW SPAL SNIETY. □ 



24 8. Instrukcje sterujqce 



7. Funkcje daty i czasu 

Interpretator gawk od wersji 3.0 posiada dwie funkcje dotyczace daty i czasu. S^ 
to systime* i strftime*: 

systimeO 

Zwraca biezacy czas w sekundach jakie uplyn^ly od poczatku epoki. W standardzie 
POSIX jest to liczba sekund od 1 Stycznia 1970 r. 

strftime ( [format) , [czas) ) 

Zwraca nap is zawierajacy [czas) (liczba w takim samym formacie jak wartosc 
zwracana przez systime*) sformatowany wedlug specyfikacji z napisu [format). 
Forma krotka strftime ()* oznacza uzycie formatu "°/ a °/ b %d °/ H:%M:°/ S °/ Z %Y" 
oraz biezacego czasu. Forma strf time ([format)) wypisuje biezacy czas wedlug 
specyfikacji z [formatu) . 

Specyfikacje przeksztalcen funkcji strftime* sa^ zgodne ze standardem AN- 
SI C. Kazda specyfikacja sklada si§ ze znaku „"/," oraz nastepujacego po nim znaku 
okreslajacego typ konwersji (por. takze instrukcja printf , w punkcie 12). Nie b§- 
dziemy podawac pelnej listy znakow konwersji (por. [ ], s. 149-151), ograniczymy 
sie^ do najcz^sciej stosowanych: 

Znaki konwersji 

Znak Typ przeksztalcenia 

d dzieii miesiaca (01-31) 

H godzina w zapisie 00-23 

I godzina w zapisie 01-12 

j dzieii roku (001-366) 

m miesiac (01-12) 

M minuta (00-59) 

S sekundy (00-61) 

y rok w zapisie dwucyfrowym (00-99) 

Y rok w zapisie czterocyfrowym (np. 1066) 

Przykladowo w wyniku wykonania ponizszego programu: 

awk 'BEGIN {print "dzisiaj jest:", strftime ( " '/.m : %d : %Y " ) > ' 

na ekranie zostanie wydrukowany napis "dzisiaj jest: 08:15:2000" (ale oczy- 
wiscie tylko wtedy gdy program uruchomimy 15 Sierpnia 2000 r.) 

8. Instrukcje sterujqce 

AWK pozwala grupowac instrukcje, podejmowac decyzje (konstrukcja if -else) 
oraz tworzyc petle (instrukcje for, while). Skladnia tych instrukcji pochodzi 
bezposrednio z j^zyka C. 



8. Instrukcje sterujqce 25 



Pojedyncza instrukcja moze bye zawsze zastqpiona lista^ instrukeji uj^tych 
w nawiasy grupujace. Na liscie instrukcje separowane sa^ znakami konca wiersza 
lub srednikami. Znaki konca wiersza moga^ pojawic sig po dowolnym lewym i przed 
dowolnym prawym nawiasem grupujacym. 

Spojrzmy przykladowo na skladniig instrukeji if -else 

if {(wyrazenie)) 
(instrukcjal) 
else 
(instrukejaz) 

czesc else (instrukejaz) jest opcjonalna. 

W celu unikni^cia dwuznacznosci przyjeto, ze kazdy else jest w parze z bez- 
posrednio poprzedzajacym go if-em; przykladowo w nastejmjacym fragmencie 
programu: 

if (el) if (e2) s=l; else s=2 

else jest w parze z drugiin if-em. (Srednik po s=l jest wymagany, gdyz else 
znalazl sig w jednym wierszu z if-em.) 

Instrukcje sterujqce 

{ [instrukcje) } 

grupowanie instrukeji. 

if ((w)) {instrukcja) 

jesli wyrazenie (w) jest prawdziwe wykonaj (instrukcje) . 

if ((«;)) (instrukcjal) else (instrukejaz) 

wykonaj (instrukcjal) jesli wyrazenie (w) jest prawdziwe, w wypadku przeciwnym 

(instrukeji) . 

while ((«;}) (instrukcja) 

jesli wyrazenie (w) jest prawdziwe wykonaj (instrukcje) i powtorz. 

for ((wl) ; (w2) ; (w3)) (instrukcja) 

rownowazne instrukeji: (wl) ; while ((w2)) {.(instrukcja); (w3)}. 

for ((zmienna) in (tablica)) (instrukcja) 

(instrukcja) jest wykonywana dla (zmiennej) przyjmujacej kolejno wartosci kazde- 

go elementu (tablicy) . 

do (instrukcja) while ((«;)) 

wykonaj (instrukcje) i jezeli wyrazenie (w) jest prawdziwe powtorz. 

break 

natychmiastowe wyjscie z pigtli while, for, do. 

continue 

rozpocznij nastejmq iteracj^ w petiach while, for, do. 



26 8. Instrukcje sterujqce 



next 

rozpocz^cie nast^pnej iteracji glownej petli wejsciowej. (Przez glownq petl§ 

wejsciowq, rozumiemy mechanizm AWK do analizowania rekord po rekordzie pliku 

wejsciowego.) 

exit (wyrazenie) 

sterowanie jest przekazywane bezposrednio do akcji END. Jesli polecenia exit uzyto 
w akcji END, program koiiczy dzialanie. Opcjonalne [wyrazenie) zwracane jest jako 
status zakoiiczenia programu. 

nextf ile* 

zakoficzenie przegladania biezacego pliku i przejscie do przegladania nast^pnego 
z podanych w linii poleceii, lub (dla ostatniego pliku) przejscie do wzorca 
END. W wyniku wykonania nextf ile* zmienia si§ wartosc zmiennej FILENAME, 
wartosciq FNR staje sie; jeden a wartosc ARGIND* jest zwiekszana o jeden. 



PRZYKLAD 10 

Wydrukowac z pliku wina.txt wino, ktorego sprzedaz przyniosla najwiekszy 
obrot. Tak postawione zadanie rozwiazuje nastgpujacy program: 

NR == 1 { omax = $NF * $(NF -1); } # inicjalizacja omax 
{ o = $NF * $(NF -1); 

if (omax < o ) { omax = o; f = $0; } 

else { if (omax == o) {f = f "\n" $0 } } 
} 
END {print omax; print f } 

Ogolny schemat dzialania programu jest taki, ze dla biezacego wiersza obliczamy 
obrot i przyrownujemy do dotychczas najwiekszego, pamietanego w zmiennej omax. 
Jezeli obrot dla biezacego wiersza jest wi^kszy od wartosci omax, to przypisujemy 
go jako nowq wartosc tej zmiennej: zapamietujemy takze wiersz, zawierajacy tq. 
wartosc (f=$0). 

Drugie polecenie if jest potrzebne na wypadek gdyby maksymalny obrot 
byl identyczny dla kilku win. Jest to wprawdzie malo prawdopodobne, ale 
mozliwe. W takim przypadku, kolejne wina sq dolaczane (konkatenowane) do 
dotychczasowej wartosci zmiennej f . 

Akcja odpowiadajac wzorcowi NR == 1 jest wykonywana tylko dla pierwszego 
wiersza i ma na celu inicjalizacja zmiennej omax. Uwazny czytelnik zapewne 
zauwazy, ze poniewaz obrot jest zawsze nieujemy, to w tym przypadku nie 
ma potrzeby jawnej inicjalizacji. Program i tak dawalby poprawne wyniki, bo 
pierwsze wystapienie omax spowodowaloby przypisanie jej wartosci 0. Gdybysmy 
jednak chcieli obliczyc zamiast obrotu maksymalnego minimalny, to ten sposob 
inicjalizacji zmiennej moze sie^ przydac. D 



9. Tablice asocjacyjne 27 



8.1. Instrukcja pusta 

Jesli w wierszu programu AWK-owego umiescimy samotny znak ' ; ', to otrzymamy 
instrukcja pustq,. Spojrzmy na ponizszy program wykorzystujacy takq instrukcja 
w petli for; program drukuje wszystkie wiersze, ktore zawierajq puste pole. 

BEGIN { FS = "\t" } # Pola oddziela znak tabulacji! 
{ for (i=l; i <= NF && $i!=""; i++) 

if (i <= NF) { print } 
} 



9. Tablice asocjacyjne 

Tablice asocjacyjne to jedyny rodzaj tablic dost^pny w AWK. Tablic nie trzeba 
deklarowac, okreslac ich wymiarow czy typu elementow skladowych. Utworzenie 
elementu tablicy nast^puje w cliwili wykonania podstawienia, np. a [44] =3.14 
lub a [1] =" Alibaba", albo innego odwolania do niego. Element nie zainicjowany 
jest rowny zero lub rowny napisowi pustemu. Indeksy nie sq liczbami ale napisami. 
Uzycie w kontekscie indeksu liczby spowoduje jej konwersj^ do odpowiedniego 
napisu. Trzeba o tym pamietec; np. print a ["01"] nie spowoduje wydrukowania 
slowa Alibaba (tylko przypuszczalnie napis pusty) - napisy "1" oraz "01" s^, 
oczywiscie rozne, podczas gdy liczby nie. 

Ponizszy program zapamietuje wszystkie slowa pliku wejsciowego oraz liczby 
ich wystapieii. 

{for (i=l; i <= NF; i++) {ls[$i]++» 

Zwrociny uwage;, ze za kazdym razem, gdy pojawia si§ nowy wyraz, AWK tworzy 
nowy element tablicy z wartoscia^ poczatkowq 0. Nast^pnie operator inkrementacji 
nadaje mu wartosc 1. Kazde nast^pne pojawienie si§ tego wyrazu powoduje 
zwiekszenie wartosci juz istniejacego elementu. 

Powstaje problem jak dobrac sie; do elementow tablicy Is, skoro nie znamy 
indeksow (wyrazow tekstu). Do tego celu sluzy specjalna forma petii for: 

for {[zmienna) in [tablica)) [instrukcja) 

[zmienna) przyjmuje iteracyjnie wszystkie wartosci indeksow [tablicy). Kolejnosc 
przegladania tablicy nie jest ustalona i jest zalezna od konkretnej implementacji 
AWK. Dzialanie petli jest nieokreslone jezeli wewnatrz petli zostanq dodane ko- 
lejne elementy do [tablicy) . Chcac wydrukowac liste; slow z omawianego przykladu 
mozemy posluzyc sie; nastejmjacq akcja^ z wzorcem END: 

END {for (word in Is) print word, Is [word] } 

Wyrazenie: 



28 9. Tablice asocjacyjne 



[indeks) in [tablica) 

pozwala ustalic czy okreslony {indeks) wyst^puje w [tablicy). Jezeli wystejmje, 
to wartosciq wyrazenia jest 1, w wypadku przeciwnym 0. Przykladowo, ponizsza 
instrukcja sprawdza czy w tablicy Is wystapilo slowo Alibaba: 

if ("Alibaba" in Is) {print "OK!"}- 
else {print "KQ!"} 

9.1. Instrukcja delete 

Element tablicy mozeniy usunac za pomocq instrukcji delete: 

delete [tablica) [[indeks)~\ 

przykladowo delete Is ["Alibaba"] usuwa z tablicy Is element odpowiadajacy 
indeksowi "Alibaba". 

PRZYKLAD 11 

Za pomocg, tablic mozna bardzo sprawnie dokonywac roznego rodzaju obliczen 
i zestawieii. Poprzednio przekonalismy sie; jak latwo mozna policzyc laczny obrot 
dla win bialych albo czerwonych. Rownie proste jest policzenie obrotow dla innych 
kategorii win. Stosowane wtedy rozwiazanie problemu jest jednak klopotliwe 
w sytuacji gdy zamiast zestawienia dla jakiegos smaku czy smakow potrzebne jest 
zestawienie obrotow z rozbiciem na wszystkie kategorie smakowe. Po pierwsze nie 
wiemy ile jest kategorii i mozeniy nie znac ich wartosci. Po drugie kategorii moze 
bye duzo. Wykorzystujac tablice asocjacyjne nie musimy posiadac tych wszystkich 
informacji, popatrzmy na zadziwiajaco krotki program rozwiazujacy problem: 

{ obrot [$(NF-2)] += $NF * $(NF-1) } 

END { for (wino in obrot) { print wino, obrot [wino] }} 

D 

9.2. Tablice wielowymiarowe 

Tablice wielowymiarowe sq, symulowane przez AWK za pomocq tablic jednowy- 
miarowych. Z punktu widzenia uzytkownika nie ma to wielkiego znaczenia. Przy- 
kladowo w wyniku dzialania ponizszego fragmentu programu: 

for (i=l; i <= 10; i++) 
for (j=l; j <= 10; j++) 
r [i, j] = randO ; 

zostanie utworzona tablica 100 elementow, do ktorych mozemy si§ odwolywac 
za pomocq par zmiennych indeksowanych postaci i,j. Wewnetrznie jednakze 
poszczegolne elementy tablic sq indeksowane za pomoca^ napisow postacji i 
SUBSEP j . Zmienna wbudowana SUBSEP przechowuje znak uzywany do oddzielenia 
indeksow skladowych; standardow^ wartosciq, tej zmiennej nie jest przecinek ale 
znak "\034". Sposob testowania czy element i, j nalezy do tablicy nie zmienia sie;: 



10. Funkcje 29 

for ((i, j) in r) {. . .} 

zas w wypadku petii for piszemy: 

for (k in r) {print r [k] } 

i, jezeli jest to potrzebne, wykorzystujemy konstrukcj^ split (k,x,SUBSEP) do 
dost^pu do wartosci zmiennych indeksowych. 

UwAGl: Elementy tablic nie moga^ bye tablicami. 

PRZYKLAD 12 

Wykorzystujac tablice wielowymiarowe nie jest trudnq rzeczq wykonanie zesta- 
wienia obrotow dla win wedlug dwu kryteriow podzialu: smaku i koloru. Ponizej 
przykladowe rozwiazanie tak postawionego problemu: 

{ o = $NF * $(NF-1); # obliczamy obrot 
smaki [$(NF-2)] += o; # obrot wg. smakow 
kolory [$(NF-3)] += o; # obrot wg. kolorow 
obrot [$ (NF-2) , $(NF-3)] += o # obrot wg. smakow i kolorow 
} 
END { for (smak in smaki) { 

print smak, "...", smaki [smak] ; 
razem +=smaki [smak] ; 
for (kolor in kolory) { 

if ((smak, kolor) in obrot) 

print " — ", kolor, "...", obrot [smak, kolor]; 
} } 
print "Razem ...", razem; 
} 

Na wydruku otrzymamy zestawienie obrotow wedlug smakow i kolorow, laczne 
obroty wedlug smakow i obroty razem. Program jest krociotki ma jednak powazna^ 
wadej porzadek drukowania zestawienia jest dowolny. Poprawieniem tego feleru 
zajmiemy si§ w przykladzie 20 (s. 39) po omowieniu polecenia printf . □ 



10. Funkcje 

AWK umozliwia definiowanie wlasnych funkeji. Definicja funkeji moze bye umiesz- 
czona w dowolnym miejscu programu, pomi^dzy kolejnymi parami wzorzec-akeja. 
Funkcje sq definiowane nastejjujaco: 

function \nazwa) (\lista-argumentow)) { 
(lista-instrukeji) } 

(lista-argumentow) to ciqg oddzielonych przecinkami argumentow funkeji. Podczas 
wywolania funkeji, argumentom nadawane sq, odpowiednie wartosci. Nazwy argu- 
mentow s^ lokalne dla funkeji; sg, one przekazywane przez wartosc, z wyjatkiem 



30 11. Wejscie 



tablic, ktore sa^ przekazywane „przez referencje" . Argumenty funkcji sq jedynymi 
zmiennymi lokalnymi w AWK. 

(Lista-instrukcji) moze zawierac instrukcje; return [wyrazenie). Wykonanie 
return polega na obliczeniu wartosci (wyrazenia) , a nastejpnie przekazaniu tej 
wartosci w miejsce wywolania funkcji (tzw. wartosc zwracana przez funkcji). 
( Wyrazenie) jest opcjonalne - jezeli go nie ma, instrukcja return jedynie 
przekazuje sterowanie do miejsca wywolania. Jezeli wsrod (listy-instrukcji) nie 
ma return to po wykonaniu ostatniej instrukcji (przed zamykajacym nawiasem 
klamrowym) sterowanie jest przekazywane do miejsca wywolania, a wartosc 
zwracana jest nieokreslona. Zilustrujmy to prostym przykladem funkcji max 
zwracajacej wi^kszy ze swoich dwu argumentow ([1], s. 53): 

function max(x, y) { return x > y ? x: y } 

Funkcje zdefmiowane za pomoca^ polecenia function mogq bye uzyte w dowolnym 
wyrazeniu, a takze wewnatrz innych funkcji; dozwolona jest takze rekursja 
(por. przyklad 13 (s. 30)). Przy wywolaniu funkcji nie mozna umieszczac odstejm 
pomiedzy jej nazwg, a rozpoczynajacym liste^ argumentow nawiasem (. 

Jak juz mowilismy tylko argumenty funcji s^ zmiennymi lokalnymi. Wszystkie 
inne zmienne s^ globalne. Jezeli chcemy aby AWK „widzial" jakas zmienne 
tylko lokalnie to jedyn^, metoda^ jest jej umieszczenie na liscie parametrow przy 
definiowaniu funkcji. Po prostu nadmiarowe parametry umieszczamy na koiicu 
listy. Nie b^da^ one wykorzystywane do przekazywania wartosci lecz bedq stanowic 
dodatkowe zmienne lokalne. Wywolanie funkcji z mniejsza^ od deklarowanej liczba^ 
parametrow jest w AWK poprawne - wszystkie nadmiarowe parametry przyjmuja^ 
wartosc rownq zero lub napisowi pustemu w zaleznosci od kontekstu. 

PRZYKLAD 13 

Nast^pujaca funkeja rekurencyjna odwraca napis poczynajac od znaku s ([6], 
s. 155). 

function rev(str, s) { 

if (s == 0) return "" 

return (substr(str, s, 1) rev(str, s-1)) 
} 

Dla wyprobowania dopiszmy nastepujacy prosty program: 
BEGIN {print revC'Karuzela" ,length("Karuzela") ) } 

□ 



11. Wejscie 

AWK moze czytac dane wejsciowe na kilka sposobow. Najprostszym jest uruclio- 
mienie go w standardowy sposob czyli, np: 



11. Wejscie 31 



awk -f [program) (plik) 

W takim kontekscie, zgodnie z tym co juz napisano wczesniej, AWK czyta 
(plik) wiersz po wierszu. Jezeli nie podamy (pliku) to AWK b^dzie czekal na 
strumiefi danych ze standardowego wejscia (klawiatury) . Cz^sto jest to dzialanie 
niezamierzone - ~C, ~D, "break czy ~Z koiiczq dzialanie programu w takiej sytuacji. 

11.1. Pola 

Standardowa^ wartoscia^ wbudowanej zmiennej FS jest " " (spacja - odsteji). W ta- 
kiej sytuacji poszczegolne pola s^ rozdzielone odstepami lub znakami tabulacji. 
Sposob rozdzielania pol mozna zmienic przypisujac zmiennej FS odpowiedni napis. 
Jezeli napis ten jest dtuzszy niz jeden znak, to AWK traktuje go jako wyrazenie re- 
gularne. Najdluzsze {leftmost longest) ciqgi znakow, nie zachodzace na siebie (non 
overlapping), pasujace do tego wyrazenia regularnego bedq oddzielac poszczegolne 
pola w biezacym wierszu. Przykladowo deklaracja: 

BEGIN {FS ="[,;:]"} 

powoduje, ze pola bedq rozdzielane przecinkiem, srednikiem lub dwukropkiem. 
Kiedy wartosciq FS jest pojedynczy znak (inny od odstejiu), to ten znak jest 
uzywany do rozdzielania pol. 

UwAGl: Wartosc zmiennej FS moze zostac nadana takze z poziomu uruchomienia 
AWK za pomocq przelacznika -F. Przykladowo zamiast powyzszego wzorca BEGIN 
moglibysmy napisac w linii polecefi: 

awk -F '[,;:] ' -f (program) (plik) 

11.2. Rekordy 

Wartosc zmiennej RS przechowuje napis uzywany przez AWK do oddzielania 
poszczegolnych rekordow. Standardowo rekordy s^ oddzielane znakami korica 
wiersza (co odpowiada przypisaniu RS="\n"). W ograniczonym zakresie mozemy 
zmienic sposob w jaki AWK wyroznia poszczegolne rekordy nadajac odpowiedni^ 
wartosc zmiennej RS. W opisie [1] separatorem rekordu moze bye tylko napis 
jednoznakowy lub napis pusty. Jezeli RS="" (napis pusty), wtedy separatorami 
rekordow sq puste wiersze (jeden lub wiecej). 

PRZYKLAD 14 

Nie nalezy do rzadkosci sytuacja, w ktorej rekordy zajmujq wiecej niz jeden wiersz; 
przykladem moze bye baza adresowa czy tez inny tego typu odpowiednik trady- 
cyjnej kartoteki papierowej. Jezeli wielkosc bazy nie jest zbyt duza, to zamiast 
specjalizowanych systemow bazodanowych z powodzeniem mozemy do jej zarza- 
dzania (wyszukiwanie rekordow, drukowanie zestawien itp.) wykorzystywac AWK. 
Zalozmy, ze plik zawiera informacje adresowe o znajomych osobach: imi§ 
i nazwisko, adres i numer telefonu, wedlug schematu, ktory ilustruje ponizszy 
fragment: 



32 11. Wejscie 



Jan Waclaw Gdanski 

ul . Boleslawa Krzywoustego 123/3 

Gdynia 80-745 

+4856 620-75-21 

Wanda Kazimiera Matysek 

ul . Bohaterow Monte Cassino 2/2 

Sopot 81-825 

+4858 551-06-50 

Wojciech Strzelecki 
Aleja Zwyci^stwa 5/2 
Gdansk 80-952 
+4858 501-26-11 

Poniewaz poszczegolne rekordy sq oddzielone pustymi wierszami, wystarczy 
odpowiednio zmienic separator rekordow, zeby bylo mozna manipulowac nimi 
bezposrednio; przykladowo program: 

BEGIN {RS=" M ; 0RS="\n\n" }; /Aleja Zwyci<=stwa/ 

wydrukuje wszystkie rekordy zawierajace napis Aleja Zwyci§stwa. Zwrocmy 
uwag£ na przypisanie DRS="\n\n"; bez niego zabrakloby pustego wiersza pomi^dzy 
kolejnymi drukowanymi rekordami. D 

W niektorych implementacjach AWK separator rekordu moze bye wyrazeniem 
regularnym. W takiej sytuacji, kazdy napis pasujacy do tego wyrazenia wyznacza 
koniec kolejnego rekordu (z tym, ze napis ten nie jest czescig, tego rekordu, 
podobnie jak w wypadku gdy separatorem jest pojedynczy znak). 

Jezeli RS jest wyrazeniem regularnym wtedy zmienna RT* przechowuje dla 
biezacego rekordu, napis b^dacy jego separatorem od rekordu nastepnego. 

PRZYKLAD 15 

Nastejpujacy program ([(>], s. 243-244) jest AWK-owg, implementacj^ edytora 
potokoweyo, tj. takiego programu, ktory czyta strumien danych, modyfikuje go 
i wysyla dalej. Sposob uzycia jest nastejpujacy: 

gawk -f awksed.awk \co) [naco) \plikl) [plikz) (...) 

Spowoduje zastajjienie frazy (wyrazenia regularnego) [co) na napis [naco) (oba 
argumenty sq wymagalne), w plikach \plikl) [plikz] .... Jezeli nie podamy listy 
plikow dane bedq czytane ze standardowego wejscia. 

function usage () { 

print "awksed co naco pliki..." > "/dev/stderr" 
exit 1 } 

BEGIN { # sprawdz argumenty wywolania 



11. Wejscie 33 



if (ARGC < 3) { usage () } 
RS = ARGV[1]; ORS = ARGV[2] 

# nie uzywaj argumentow jako nazw plikow 

ARGV[1] = ARGV[2] = "" 
} 
{ if (RT == ""){ printf "%s" , $0 } 

else { print } } 

Idea dzialania jest prosta: separatorem rekordow jest \co) a separatorem rekordow 
na wyjsciu (naco) . Problemem jest jedynie sytuacja, w ktorej ostatni rekord 
nie konczy sie^ napisem pasujacym do RS. Jezeli plik nie konczy si§ napisem 
pasujacym do RS to zmienna RT* bedzie rowna napisowi pustemu. Stad, warunek 
if (RT=="") . . . gwarantuje wydrukowanie calej zawartosci pliku wejsciowego. 

Drugi ciekawy fragment tego przykladu to przypisanie ARGV[1] = ARGV[2] 
= "". Chodzi o to, zeby AWK nie traktowal napisow \co) i \naco) jako nazw 
plikow wejsciowych. Dokladne wyjasnienie znaczenia tego wiersza znajduje si§ 
w punkcie 13. □ 

PRZYKLAD 16 

Uwazny czytelnik zauwazyl, ze przegladanie bazy z przykladu 14 (s. 31) nie jest 
wolne od bl^dow, np. podajac: 

BEGIN {RS=" M ; 0RS="\n\n" }; /Gdansk/ 

nie tylko zostana^ wydrukowane osoby z Gdanska, ale takze Jan Waclaw Gdaiiski. 
Zeby uniknac podobnych bledow niusimy w jakis sposob okreslic znaczenie 
poszczegolnych fragmentow rekordu. Mozemy ustalic, np. ze imie; i nazwisko 
zajmuje pierwszy wiersz, adres drugi, telefon trzeci itd. Mozna tez zastosowac 
schemat klucz-wartosc, wedlug ponizszego przykladu: 

kto Jan Waclaw Gdanski 

tel +4856 620-75-21 

email j s . gdanskiOrugger . gdynia . pi 

kto Wanda Kazimiera Matysek 
tel +4858 551-06-50 

kto Wojciech Strzelecki 
miasto Gdansk 80-952 
adres Aleja Zwyci^stwa 5/2 
tel +4858 501-26-11 
email w.strzelecki@ws.com.pl 

W ten sposob rekordy moga^ zawierac rozne pola a ich porzadek jest dowolny. 
Pozwala to na latwe modyfikowanie bazy za pomocq zwyklego edytora tekstowego; 
nie niusimy pamietec struktury bazy -jest ona samoidentyfikujaca si<g. Latwo takze 



34 11. Wejscie 



dodawac, w miare; potrzeb, nowe pola. A jak przegladac takie pliki? Jednym ze 
sposobow jest wykorzystanie tablic asocjacyjnych: 

#! /bin/bash 

cat $3 I awk -vCD=$l -vC0CD=$2 ' 

BEGIN {FS="\t" } # Pierwsze pole od reszty oddziela tabulator 

NF > {dane[$l] = $2 } 

NF < 1 && dane[CD] ~ COCO { drukuj () ; usunO } 

END { if (dane[C0]~ COCO) {drukuj () }} # ostatni rekord! 

function usun(i){ for (i in dane) {delete dane[i] } } 

function drukuj (){ print dane["kto"]; print dane["tel"] }' 

Dzialanie programu jest nastejiujace: kolejne wiersze zapamietujemy w tablicy 
dane wedlug schematu: dane \_[klucz)l = [wartosc). Aby uproscic czytanie plikow 
umawiamy sig, ze pierwsze pole jest oddzielone od nast^pnych znakiem tabulacji. 
Po napotkaniu pustego wiersza (NF<1) sprawdzamy czy odpowiedni element tablicy 
pasuje do wzorca poszukiwah (dane [CO] ~ COCO). Jezeli tak, to wykonywana jest 
procedura drukowania rekordu (funkcja drukuj) a nast^pnie usuwamy wszystkie 
elementy z tablicy dane (funkcja usun). Po ostatnim rekordzie moze nie bye 
pustego wiersza, stad koniecznosc dodania wzorca END. Zmiennym CO (nazwa pola) 
i COCO (wartosc pola) przypisujemy wartosci wykorzystujac opeje; -v wywolania, 
por. punkt 14. Dla wygody program zostal umieszczony w skrypcie shellowym, 
wiec jego uruchomienie wygl^da nastejiujaco (szukaj jest nazwfj skryptu): 

szukaj miasto Gdansk (plik-adresowy) 

W rezultacie zostanie wydrukowany wylacznie Wojciech Strzelecki. □ 

11.3. Instrukcja getline 

Polecenie getline umozliwia czytanie danych z biezacego lub/i z innego pliku 
tekstowego albo z potoku generowanego przez inny program. Ponizej zestawiono 
rozne formy uzycia getline (\plik) i (proyram) to zmienna lub stala napisowa 
zawierajaca odpowiednio nazwe^ pliku lub programu, z ktorego AWK ma czytac 
strumieri danych). 

getline 

Postac instrukeji Inicjalizowane zmienne 

getline $0, NF, NR, FNR 

getline (z) (z), NR, FNR 

getline < (plik) $0, NF 

getline \z) < {plik) \z) 

[proyram) I getline $0, NF 

[proyram) I getline \z) \z) 

Dwie pierwsze formy dotyczq czytania danych z biezacego pliku, dwie nast^pne 
z [pliku). Dwie ostatnie to wczytywanie danych ze strumienia generowanego 



11. Wejscie 35 



przez inny (proyram). Polecenie getline zwraca wartosc 1 jezeli wczytany zostal 
nast^pny wiersz tekstu (rekord), jezeli napotkano koniec pliku (strumienia) 
danych oraz — 1 w wypadku napotkania bl^du (np. otwarcia pliku). Jezeli po slowie 
getline wystejiuje zmienna (z) to wczytany wiersz jest dostepny jako wartosc tej 
zmiennej, w innym wypadku jest dostejmy jako wartosc zmiennej $0. Przykladowo 
petla: 

while (getline < (plik) > 0) {...} 

Jest „tradycyjna" technikg, umozliwiajaca^ przejrzenie calego (pliku). Poszczegolne 
wiersze dostepne sej, w kazdej iteracji p^tli jako wartosci zmiennej $0. 

Podobnie wyglada czytanie danych z potoku. Przykladowo chcac przekazac do 
AWK zawartosc biezacego katalogu mozemy sie^ posluzyc nastepujaca^ petla; 

while ("Is -1" I getline > 0) {...} 

Pliki i potoki otwarte przez AWK sq automatycznie zamykane z chwilq zakoficzenia 
dzialania programu. Jezeli jednak musimy przegladac wielokrotnie zawartosc 
jakiegos pliku w obrebie jednego programu AWK-owego to za kazdym razem nalezy 
zamknac czytany plik uzywajac funkcji close, np.: 

close ((plik)); closeC'ls -lrt") 

PRZYKLAD 17 

Ponizszy program wyswietli list§ wszystkich plikow wiekszych od DUZY. 

BEGIN { niema = 1; DUZY = 1000000 

while ("Is -1" I getline) 

if (NF == 9 && $5 > DUZY) {print $0; niema =0 } 

if (niema) {print "Nie ma plikow wiekszych od" , DUZY; } 
} 

Instrukcja if najpierw testuje czy wczytany wiersz zawiera dokladnie 9 pol co 
pozwala „odcedzic" pola nie zawierajace informacji o plikach (naglowek listy). 
Nast^pnie sprawdzany jest warunek czy wielkosc pola jest wieksza od DUZY. D 

PRZYKLAD 18 

Ponizszy funkcja pobiera biezacq dat§ z komputera i udostepnia j^ w trzyelemen- 
towej tablicy DAT, ktorej element "year" zawiera rok, element "mon" - miesiac 
a element "day" - dzieii. 

function getdate(x, date) { 

"date" I getline x; split (x, date, " "); 

DAT ["year"] = date [6]; DAT ["mon"] = date [2]; DAT ["day"] = date [3] ; 

return; 
} 

# Przyklad wykorzystania funkcji getdate: 
BEGIN {getdate (); print DAT ["mon"], DAT ["day"] } 



36 12. Instrukcje wyjscia — print/printf 



Argumenty x, i date nie sluzq do przekazywania wartosci, ale do uczynienia obu 
zmiennych lokalnymi (por. uwagi na ten temat w punkcie 10) - funkcje^ wywolujemy 
po prostu getdateO. Zwracamy uwag§, ze program jest „nieodporny" na zmiane^ 
formatu drukowanej daty. □ 



11.4. Pola o ustalonej dlugosci 

Rekord moze bye takze dzielony na pola o ustalonej dlugosci. W tym celu zmiennej 
wbudowanej FIELDWIDTHS* przypisujemy napis zawierajacy ciqg oddzielonych 
odstejiami liczb. Kazda liczba oznacza dlugosc odpowiedniego pola w znakach. 
Jezeli wartosciq zmiennej FIELDWIDTHS* nie jest napis pusty, pola wyznaczane sg, 
w oparciu o specyfikacje^ podana^ w tej zmiennej, a nie w oparciu wartosc separatora 
pol (czyli zmiennq FS). Przypisanie wartosci zmiennej FS (np. FS=FS) przywraca 
standardowy sposob wyznaczania pol. 

PRZYKLAD 19 

Rekordy dotyczace podjazdow w pliku tdf2000.txt maja^ pola o ustalonej 
dlugosci, zatem do ich podzielenia mozna wykorzystac zmiennq FIELDWIDTHS*: 

# Drukuj nazw§ gory i jej dlugosc 

BEGIN { FIELDWIDTHS = "1 25 5 5 4 3 4 4 4 4 4" } 

NR==1, /"-+[ \t]*$/ { next } 

/"Etap[ \t]+[0-9]+:/ { next > 

NF > { print $2, $11 } 

W rezultacie wykonania programu zostanq wydrukowane nazwy gor i dlugosci 
podjazdow. □ 

UwAGl: Zwrocmy uwag§, ze gawk nie dokonuje zadnego sprawdzenia poprawnosci 
specyfikacji podanej w napisie FIELDWIDTHS*. 



12. Instrukcje wyjscia — print/printf 

Instrukcje print oraz printf sluzq do drukowania. Pierwsza z nich drukuje swoje 
argumenty zawsze wedlug tego samego formatu, druga umozliwia precyzyjniejsze 
sterowanie postaci<| wypisywanych danycli. Dane mog<| bye drukowane na ekran, 
do pliku lub w potoku. 



12.1. Instrukcja printf 

Skladnia instrukeji printf jest niemalze identyczna z odpowiedni^ funkeja^ 
jezyka C. Ogolna postac tej instrukeji jest nastejmjaca: 

printf ((format) , (aryl) , \ary2) , . . . ) 



12. Instrukcje wyjscia — print/printf 37 



Nawiasy ( oraz ) sa^ opcjonalne. Napis lub zmienna napisowa (format) okresla spo- 
sob przeksztalcania i formatowania argumentow. Zawiera on dwa rodzaje obiek- 
tow: zwykle znaki, kopiowane po prostu przy drukowaniu oraz specyfikacje prze- 
ksztalcen, z ktorych kazda okresla sposob przeksztalcenia i wypisania kolejnego 
argumentu funkcji printf . Specyfikacja ta rozpoczyna sie^ od znaku % a koiiczy 
znakiem okreslajacym typ konwersji. Pomiedzy nimi mozemy uzyc ponadto nast§- 
pujacych znakow modyfikujacych: 

• -, zawartosc pola jest justowana do lewego kranca pola; 

• (ciqg-cyfr) , okreslajacy minimalny rozmiar pola (w znakach). Przeksztalcony 
argument bedzie wpisany do pola o dlugosci co najmniej rownej (ciqg-cyfr) . 
Jezeli argument sklada sie^ z mniejszej liczby znakow, to bedzie ono uzupel- 
nione do dlugosci minimalnej odst^pami. Jezeli specyfikacja dlugosci rozpo- 
czyna si§ cyfra^ 0, to pole bedzie wypelniane nie znaczacymi zerami; 

• .(ciqg-cyfr), maksymalna drukowana dlugosc napisu lub liczba cyfr po 
kropce dziesietnej. 

Oto lista znakow przeksztalceii i ich znaczenie (zapis [-] oznacza, ze znak - jest 
opcjonalny): 

Znaki konwersji 

Znak Typ przeksztalcenia 

c znak 

d liczba calkowita 

e liczba postaci [-]d.ddddddE[-]dd 

f liczba postaci [-Jddd.dddddd 

g e lub f , w zaleznosci od tego, ktore jest krotsze bez nieznaczacych zer 

o liczba osemkowa bez znaku 

s napis 

x liczba szestnastkowa bez znaku 

Jezeli znak wystepujacy po % nie jest znakiem przeksztalcenia to jest on po 
prostu wypisany; zatem °/ °/o spowoduje wypisanie znaku %. 

Ponizsze zestawienie ilustruje dzialanie roznych specyfikacji. Aby mozna ocenic 
dlugosci pol otoczono je znakami I , zas spacje oznaczono znakiem ". 

Przyklady specyfikacji 

Wynik 
| 33%| 

Ml 
1331 
I 33 I 

|3.141500e+000| 
13.1415001 



Specyfikacja 


Argumt 


°/o5d'/o% 


33.33 


%c 


33.33 


%d 


33.33 


'/.5d 


33.33 


%e 


3.1415 


%f 


3.1415 



38 12. Instrukcje wyjscia — print/printf 



7,8. 3f 3.1415 | 3. 141 1 

7,08. 3f 3.1415 10003.1411 

7,s Alibaba I Alibaba I 

7,9s Alibaba |~~Alibaba| 

7,-9s Alibaba I Alibaba"" I 

%-.3s Alibaba I Alii 

7,-9. 3s Alibaba |Ali I 



12.2. Instrukcja print 

Instrukcja print jest uproszczona^ formq printf a jej dzialanie jest nastepiujace: 
drukowane argumenty sa_ oddzielane wartosciq zmiennej wbudowanej OFS (stan- 
dardowo 0FS=" " - argumenty oddzielone sq znakiem odst^pu) a na koiicu listy 
jest dmkowana wartosc zmiennej wbudowanej DRS (standardowo 0RS="\n" - kazde 
kolejne print drukuje od nowego wiersza). Wszystkie argumenty numeryczne sa^ 
drukowane w oparciu o t§ sama^ specyfikacje^ okreslonq poprzez wartosc zmiennej 
OFMT (standardowo DFMT="7o. 6g"). Stad, uruchamiajac ponizszy przyklad: 

awk 'BEGIN {0FS= M : " ; 0RS="->" ; print log(2), log(3) ; print log(5) ; }' 

otrzymamy: 

. 693147 : 1 . 09861->1 . 60944-> 

UwAGl: print to skrot od print $0 (a nie, jak mozna by siij spodziewac, print 
0RS). 

12.3. Drukowanie do plikow 

Zamiast na ekran (standardowe wyjscie) instrukcje printf /print mogq przeslac 
dane do pliku. Sluzq do tego operatory > oraz >>, zas instrukcje majq wowczas 
postac: 



printf (format) , (argl) , . . . > (plik) 
print (argl) , . . . > (plik) 

lub 

printf (format) , (argl) , . . . » (plik) 
print (argl) , . . . >> (plik) 

(plik) jest napisem lub zmienna^ typu napisowego zawierajaca^ legalnq (z punktu 
widzenia systemu operacyjnego) nazwe^ pliku. Przykladowo program: 

{ printf "7,s\n", $0 > $1 } 

b^dzie dzialal doputy, dopoki wartosciq, $0 w pliku wejsciowym bedzie napis 
mogacy bye legalnq nazwq pliku (oraz dopoki liczba otwartych jednoczesnie plikow 
nie przekroczy dopuszczalnego maksimum, porownaj uwagi z przykladu 22 (s. 41)). 



12. Instrukcje wyjscia — print /printf 39 



UwAGl: Instrukcja printf "%d 7.d\n" , $1, $2 > $3 spowoduje wydrukowa- 
nie $1 i $2 do pliku okreslonego jako wartosc pola $3, a nie $1 i wyni- 
ku porowania wartosci drugiego oraz trzeciego pola. Jezeli chcemy osiqgnac 
to drugie to powinnismy napisac: printf "7od %d\n" , $1, ($2 > $3) albo 
printf ("7,d %d\n" , $1, $2 > $3). 

Operator > otwiera plik tylko raz (niszczac poprzedniq zawartosc); kolejne 
instrukcje printf dodaja^ tekst do tego pliku. Operator >> rozni sie; od > tym, 
ze przy otwarciu pliku poprzednia zawartosc nie jest niszczona. 

PRZYKLAD 20 

Przyklad 12 (s. 29) zawiera program drukujacy zestawienie obrotow dla win z po- 
dzialem wedlug smaku i koloru. Powaznq wada^ programu jest niemoznosc okresle- 
nia porzadku, w jakim ma on drukowac poszczegolne pozycje zestawienia. Ponizej 
poprawiona wersja drukujqca poszczegolne rubryki w porzadku alfabetycznym: 

# ! /bin/bash 

if test $# -eq 0; then 

echo "'basename $0': plik..." >&2; exit 0; fi 

cat $* I awk ' { o = $NF * $(NF-1); 

smaki[$(NF-2)] += o; kolory [$(NF-3)] += o; 
obrot[$(NF-2), $(NF-3)] += o 
} 
END { for (smak in smaki) 

for (kolor in kolory) 

if ((smak, kolor) in obrot) 

print smak, kolor, obrot [smak, kolor]; 

}' I sort I awk ' BEGIN { kreska = "+ +" 

KRESKA = "+=======================+"; print KRESKA; } 

{ if (kolor != $1) { 
if (NR !=1) { 

printf "I Razem: I '/,10.2f I \n" , razem; 
print KRESKA; razem = 0} 
printf "I Wina 7.-16. 16s I \n" , $1; 
print kreska; 
} 
razem += $3; rrazem += $3; kolor = $1; 
printf "I 7.-8. 8s I %10.2f l\n", $2, $3 
} 

END { printf "I Razem: I 7.10. 2f I \n" , razem; 
print KRESKA; 

printf "I DG0EEM: I 7.10. 2f I \n" , rrazem; 
print KRESKA; } ' 

Problem zostal rozwiazany z wykorzystaniem dwoch skryptow AWK-owych oraz 
standardowego programu sort. Pierwszy skrypt dokonuje obliczeii a wyniki 



40 12. Instrukcje wyjscia — print/printf 



drukuje w formacie odpowiednim dla programu sort, ktory jest umchamiany 
w kolejnym kroku. Wydrukowaniem posortowanych danych w formie eleganckiej 
tabelki zajmuje sie^ drugi skrypt AWK-owy. Calosc zostala „zintegrowana" poprzez 
polaczenie skryptow oraz programu sort w potok i umieszczenie wszystkiego 
w jednym skrypcie shellowym. (Skrypt ten oczekuje nazw plikow do przeczytania 
jako argumentow wywolania. Instrukcja: if test $# -eq 0;... fi sprawdza 
czy podano jakies argumenty; jezeli nie to skrypt konczy dzialanie. Polecenie 
cat $* wysyla strumieii danych na wejscie pierwszego programu AWK-owego. 
Przypominamy, ze $* w j^zyku bash oznacza liste^ wszystkich argumentow, 
z ktorymi uruchomiono skrypt.) D 

12.4. Drukowanie w potoku 

Mozliwe jest takze drukowanie w potoku za pomocq instrukcji printf (print) 
o postaci: 

printf (format) , (aryl) , . . . I [program) 
print (argl) , . . . I (proyram) 

[program] jest napisem lub zmienna^ napisow^ zawierajacq nazwe; programu-odbiorcy 
strumienia danych drukowanych przez printf (print). 

PRZYKLAD 21 

Jako ilustracje; zmodyfikujemy dwuwierszowy program z przykladu 11 (s. 28), tak 
zeby poszczegolne kategorie byly drukowane wedlug malejacych obrotow: 

{ obrot[$(NF-2)] += $NF * $(NF-1) } 

END {for (wino in obrot) {print wino, obrot [wino] I "sort -nr +1" }} 

Dla przypomnienia sort -nr +1 oznacza sortowanie numeryczne, w odwrotnym 
porzadku poczynajac od drugiego pola. D 

12.5. Funkcja close 

Funkcja close zamyka plik otwarty za pomocq operatorow >, >> i I . Jej skladnia 
jest nastepujaca: 

close {(napis)) 

gdzie [napis) jest identyczny z napisem (plik) lub (proyram), za pomocq ktorych 
uprzednio otwarto plik/potok. Funkcja ta jest niezbedna w sytuacji gdy np. naj- 
pierw piszemy do pliku a nast^pnie chcemy (w obr^bie tego samego programu) 
czytac z niego dane. 

Napis zamykajacy plik lub potok musi bye identyczny z dokladnosciq do 
znakow odstgpu, z tym, za pomocfj ktorego plik/potok zostal otworzony. Z tego 
wzgledu zalecamy uzywanie do otwierania i zamykania plikow i potokow uprzednio 
zadeklarowanych zmiennych a nie poslugiwanie sie^ stalymi napisowymi. 



12. Instrukcje wyjscia — print /printf 41 



PRZYKLAD 22 

Nalezy przepisac zawartosci pliku tdf2000.txt w taki sposob aby kazdy etap 
zostal zapisany w odzielnym pliku. Ponizszy program jest jednym z mozliwych 
sposobow rozwiazania problemu: 

# ! /usr/bin/awk -f 

NR==1, /"-+[ \t]*$/ { next } # Pomin naglowek, 

/"Etap[ \t]+[0-9]+:/ { close (File); 

sub(/:/,"",$2) ; File="etap" $2; 

next } 
NF > { print $0 > File} 

Instrukcja File="etap" sub(/ : /, " " ,$2) tworzy nazwe; pliku wedlug schematu 
eta.p( numer-etapu) . Kolejne niepuste wiersze (NF > 0) bedq wysylane do pliku 
eta.p (numer-etapu) . Polecenie close (File) zamyka plik zwiazany z poprzednim 
etapem. W tym przykladzie close nie jest potrzebna bo bedziemy pisac raptem 
do trzech plikow. Gdyby jednak etapow bylo wiecej to mogloby si§ okazac, 
ze probujemy otworzyc wiecej plikow niz jest to dopuszczalne (dopuszczalna 
maksymalna liczba otwartych plikow jest zalezna od ustawieii systemowych) 
i program zakoiiczylby si§ bl^dem. 

Wazna jest kolejnosc: najpierw zamykamy plik (poprzedni) a dopiero w kolejnej 
iteracji otwieramy nast^pny. Dla pierwszego etapu nie ma wprawdzie czego 
zamykac - ale w AWK proba zamkni^cia pliku, ktory nie jest otwarty nie jest 
bledem. D 

PRZYKLAD 23 

Aby obliczyc procentowy udzial obrotu kazdego wina w obrocie ogolem (plik 
wina.txt) nalezy przeczytac plik dwa razy. (Mozna tez probowac wczytac caly 
plik do pami^ci, ale takie rozwiazanie jest bardziej skomplikowane a takze, dla 
bardzo dlugiej listy win, moze zabrakn^c pamieci.) 

#! /bin/bash 

if [ ! -f "$1" ] ; then 

echo "Wykorzystanie : 'basename $0': plik" >&2; exit 0; fi 

awk 'FNR==1 { pass++ } 

pass == 1 {oo += $NF * $(NF-1) } 

pass == 2 {print $0, 100 * $NF * $(NF-l)/oo }' $1 $1 

Trik polega na dwukrotnym umieszczeniu tego samego pliku jako argumentow 
wywolania AWK. Przypominamy, ze zmienna FNR zawiera numer biezqcego 
rekordu, liczqc w kazdym pliku wejsciowym od pocz^tku. Licznik pass jest 
zwiekszany o 1 po napotkaniu pierwszego wiersza kazdego pliku zatem ma on 
wartosc 1 przy pierwszym i 2 przy drugim czytaniu pliku. W konsekwencji druga 
akcja b^dzie wykonywana dla kazdego wiersza podczas jego pierwszego czytania 
zas trzecia podczas drugiego. Takie rozwiazanie dwukrotnego czytania tego samego 
pliku pozwala znacznie uproscic program: nie musimy jawnie czytac pliku w petii 



42 13. Argumenty wywolania programu 



while za pomoca^ getline, korzystac z funkcji split i zamykac plik za pomoca^ 
close. D 



12.6. Funkcja ff lush* 

Funkcja f flush* o postaci: 

f flush {[napis)) 

oproznia bufor zwiazany z plikiem lub potokiem poprzez [napisj, identyczny z na- 
pisem (plik) lub [program] , za pomocq, ktorych uprzednio otwarto plik/potok. Do- 
puszczalne sg, takze dwie nastejmjace postacie funkcji: f f lushO oraz f f lush(" ") . 
Pierwsza oproznia bufor zwiazany ze standardowym wyjsciem, druga bufory zwi^- 
zane z wszystkimi otwartymi w danej chwili plikanii/potokami. 

12.7. Funkcja system 
Funkcja system ma postac: 

system ( [instrukcja) ) 

wykonuje instrukcja systemow<| przekazanq jako wartosc napisowego argumentu 
[instrukcja). Najcz^sciej funkcje printf /printf i operatory >, >> i I wystarczaja^ 
do zrealizowania typowych zadaii bez potrzeby uciekania si^ do funkcji system. 

13. Argumenty wywolania programu 

Wartosci argumentow wywolania programu sq, podobnie jak w wypadku j^zyka C, 
przechowywane we wbudowanej zmiennej tablicowej ARGV. Zmienna ARGC zas 
zawiera liczbe; argumentow. Przykladowo: 

gawk -v v=l -f l.awk test.txt v=l b 

ARGC ma wartosc 4, ARGV[0] ="gawk", ARGV[1] ="test . txt", ARGV [2] ="v=l", 
ARGV [3] ="b". Opcje i ich wartosci nie sq liczone (w przykladzie s^ to -f 
i -v) a ARGV[0] jest rowna nazwie uruchomionego programu (tu gawk). Ponizszy 
program wypisuje wartosc wszystkich argumentow wywolania: 

BEGIN {for (i=0; i< ARGC; i++) {print ARGV[i]} } 

Zmienne ARGC i ARGV mozna zmieniac wewnatrz programu. Po napotkaniu 
koiica biezacego pliku wejsciowego AWK pobiera nastepny element tablicy ARGV 
jako nazw§ nastejmego pliku wejsciowego. Przykladowo: 

BEGIN{ ARGV [l]="test. txt"; if (ARGC < 2) {ARGC = 2} } 



14. Uruchamianie AWK 43 



spowoduje, ze AWK, zawsze b^dzie przeszukiwal jako pierwszy plik „test.txt" 
bez wzgl^du na to jaki (i czy w ogole) podamy w linii polecen. 

Aby usunac nazwe^ pliku z tablicy ARGV mozemy albo nadac odpowiedniemu 
elementowi tablicy wartosc napisu pustego, albo posluzyc si§ poleceniem delete. 
Napis „-" oznacza standardowe wejscie. Tego typu manipulacje z reguly umiesz- 
czamy wewnatrz wzorca BEGIN, por. przyklad 15 (s. 32). 

PRZYKLAD 24 

W przykladzie 17 (s. 35) wielkosc pliku byla zadeklarowana na stale co jest pewna^ 
niedogodnosciq: ponizsza modyfikacja pozbawiona jest juz tej wady: 

BEGIN { flag = 1; 

if (ARGC > 1) {DUZY = ARGV[1] * 1000} else { DUZY = 0; }; 

while ("Is -1" I getline > 0) { 

if (NF == 9 && $5 > DUZY) {print; flag =0 }} 

if (flag) {print "Nie ma plikow wi^kszych od" , DUZY; } 
} 

Chcac uzyskac listig plikow np. wiekszych od 500kb, wystarczy teraz napisac awk 
-f chkbig.awk 500 (gdzie chkbig.awk zawiera powyzszy kod). □ 



14. Uruchamianie AWK 

AWK mozna wywolac z kilkunastoma opcjami. Najwazniejsze z nich to -f , -F oraz 
-v. Dwie pierwsze juz omowilismy, ostatnia umozliwia nadanie zmiennej (ogolnie 
zmiennym, poniewaz mozemy j^ uzywac wielokrotnie) wartosci w momencie 
uruchomienia programu (z linii poleceii). Przyklad 25 (s. 43) ilustmje sposob 
wykorzystania opcji -v. 

UwAGl: Z czasem gdy dorobimy si§ biblioteki wlasnych funkcji AWK-owych, moze 
powstac problem, jak w elegancki sposob dolaczac jq, do roznych plikow, w taki 
sposob, jak umozliwia to, np. instrukcja #include w C? Okazuje siig, ze opcja -f 
nie musi wcale wyst^powac tylko raz: 

awk -f mylib.awk -f [program) [plik) 

gdzie plik mylib.awk zawiera bibliotek^ naszych funkcji. Nie jest to rozwiazanie 
idealne ale - wedlug nas - najlepsze z mozliwych. 

Zmienna srodowiskowa AWKPATH zawiera katalogi, w ktorych AWK szuka 
plikow zrodlowych podanych za pomocg, opcji -f . 

PRZYKLAD 25 

Problem z przykladu 17 (s. 35) mozna rozwiazac w inny sposob niz ten zaprezen- 
towany w przykladzie 24 (s. 43) umieszczajac zmodyfikowany program AWK-owy 
(oczywiste zmiany pozostawiamy czytelnikom) w skrypcie shellowym: 

#! /bin/bash 

awk -vDUZY=$l 'BEGIN { (...) }' 



44 



Skorowidz 



Zeby otrzymac list§ plikow wiekszych od np. 600kb wystarczy teraz napisac: 
chkbig 600 (gdzie chkbig jest nazwq skryptu). □ 



Bibliografia 

[1] Aho Alfred V., Kernighan Brian W., Weinberger Peter J.: The AWK Program- 
ming Language, Addison- Wesley 1988. 

[2] Alio Alfred V., Kernighan Brian W., Weinberger Peter J.: AWK - A Pattern 
Scanning and Processing Language, 1978. Dostejmy m.in. w http : //www . 
softlab.ntua.gr/unix/docs/awk.ps jako plik postscriptowy. 

[3] comp.lang.awk FAQ. Dokument dostejiny m.in. w http://www.faqs.org/ 
f aqs/computer-lang/awk/f aq. 

[4] Kernighan Brian W., Ritchie Denis M.: J^zyk C, WNT 1988. 

[5] Lichohski Boguslaw, Przechlewski Tomasz: AWK - opis j^zyka z przykladami, 
Biuletyn GUST 7/1996, s. 10-25. 

[6] Robbins Arnold D.: Effective AWK Programming. A User's Guide for GNU 
AWK, version 1.0.4, April 1999. Podr^cznik rozpowszechniany w pakiecie 
z programem gawk, dostepny m.in. w ftp://sunrise.pg.gda.pl/pub/gnu/ 
gawk. 

Skorowidz 

Hasla oznaczone * oznaczajq rozszerzenia standardu AWK, zas oznaczone zna- 
kiem t funkcje zdefiniowane przez autora. 



#, 10 

Aho, Alfred, 5 
ARGC, 42 
ARGIND*, 16, 26 
ARGV, 42 
atan2, 20 
AWKPATH, 4.3 

BEGIN, 10, 43 
break, 25 
Brennan Michael, 6 

close, 35, 40 
continue, 25 
cos, 20 

delete, 28, 43 



do, 25 
DOS, 5, 22 

END, 10, 26 
ENVIRON*, 16 
ERRN0*, 16 
exit, 26 
exp, 20 

f flush*, 42 

FIELDWIDTHS*, 16, 36 

FILENAME, 16, 26 

FNR, 16, 26, 34, 41 

for, 25 

FS, 7, 10, 16, 21, 31 

funkcje, 29 

— rekurencyjne, 30 



gensub*, 16, 21 
getline, 34, 35 
gsub, 16, 20, 22 

if, 25 

IGN0RECASE*, 16 
index, 16, 21 
int, 20 
ISO-8859-1, 16 

Kernighan, Brian, 5 
komentarz, 10 

leapyear 1 ", 18 
length, 21 
log, 20 



Skorowidz 



45 



match, 16, 21 
max 1 ", 30 
modulo, 17 

next, 14, 26 
nextfile*, 26 

NF, 8, 16, 17 

OFMT, 38 

OFS, 17, 38 

opcje wywolania, 43 

ORS, 32, 38 

print, 38 
printf , 36 

rand, 20 

return, 30 

RLENGTH, 21 

Robbins, Arnold, 24, 30, 

32 



RS, 7, 16, 31-33 
RSTART, 21 
RT*, 32, 33 

sin, 20 
split, 16, 21 
sprintf , 21 
sqrt, 20 
srand, 20 
strftime*, 24 
sub, 16, 17, 22 
SUBSEP, 28 
substr, 22 
system, 42 
systime*, 24 

tablice 

— asocjacyjne, 27, 29, 34 

— wielowymiarowe, 28 



tolower*, 22 
toupper*, 22 

upper 1 ", 23 

Weinberger, Peter, 5 
while, 25 
wyrazenie 

— regularne, 12, 15 
wzorzec, 5 

- BEGIN/END, 9 

— regularny, 11 

— z przecinkiem, 10, 12 

— zlozony, 9, 11 

zmienna 

— globalna, 30 

— lokalna, 30 

— srodowiskowa, 43 

— wbudowana, 16